Cocos2d-x程序在Android下的启动过程

注:原文也在公司内部论坛上发了

本文通过分析cocos2d-x(分析版本为cocos2d-x-2.2.1)自身提供的示例程序HelloLua(在目录$(sourcedir)\samples\Lua\HelloLua\下)来分析cocos2d-x的在android平台下的具体启动过程。

我们知道android平台的游戏是从一个Activity开始的。HelloLua的启动Activity是在文件HelloLua.java中定义的,相关代码如下:

public class HelloLua extends Cocos2dxActivity{
	protected void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
	}

	public Cocos2dxGLSurfaceView onCreateGLSurfaceView() {
		return new LuaGLSurfaceView(this);
	}

	static {
        System.loadLibrary("hellolua");
   }
}

从上面的代码,启动Activity是继承Cocos2dxActivity的,在游戏启动时,Activity首先会执行静态代码块,加载libhellolua.so库,这个动态链接库是在用NDK编译的时候生成的;然后就是执行onCreate方法,这里调用了父类Cocos2dxActivity的onCreate方法。 Cocos2dxActivity在

$(sourcedir)\cocos2dx\platform\android\java\src\org\cocos2dx\lib\Cocos2dxActivity.java

中定义,其OnCreate方法代码如下:

protected void onCreate(final Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	sContext = this;
	this.mHandler = new Cocos2dxHandler(this);

	this.init();

	Cocos2dxHelper.init(this, this);
}

这里主要是执行初始化过程,Cocos2dxHandler主要处理显示Dialog的消息,Cocos2dxHelper是个辅助类,我们主要看init()方法,代码如下:

public void init() {

    	// FrameLayout
        ViewGroup.LayoutParams framelayout_params =
            new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                                       ViewGroup.LayoutParams.FILL_PARENT);
        FrameLayout framelayout = new FrameLayout(this);
        framelayout.setLayoutParams(framelayout_params);

        // Cocos2dxEditText layout
        ViewGroup.LayoutParams edittext_layout_params =
            new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
                                       ViewGroup.LayoutParams.WRAP_CONTENT);
        Cocos2dxEditText edittext = new Cocos2dxEditText(this);
        edittext.setLayoutParams(edittext_layout_params);

        // ...add to FrameLayout
        framelayout.addView(edittext);

        // Cocos2dxGLSurfaceView
        this.mGLSurfaceView = this.onCreateView();

        // ...add to FrameLayout
        framelayout.addView(this.mGLSurfaceView);

        // Switch to supported OpenGL (ARGB888) mode on emulator
        if (isAndroidEmulator())
           this.mGLSurfaceView.setEGLConfigChooser(8 , 8, 8, 8, 16, 0);

        this.mGLSurfaceView.setCocos2dxRenderer(new Cocos2dxRenderer());
        this.mGLSurfaceView.setCocos2dxEditText(edittext);

        // Set framelayout as the content view
        setContentView(framelayout);
}

init()方法就是为Activity绑定View Hierarchy,这里的FrameLayout是根View,FrameLayout又包含一个Cocos2dxEditText和一个Cocos2dxGLSurfaceView,这个Cocos2dxGLSurfaceView的实例在方法onCreateView中创建的,代码非常简单,如下:

public Cocos2dxGLSurfaceView onCreateView() {
	return new Cocos2dxGLSurfaceView(this);
}

而类Cocos2dxGLSurfaceView本身是在

$(sourcedir)\cocos2dx\platform\android\java\src\org\cocos2dx\lib\Cocos2dxGLSurfaceView.java

中定义,它继承自opengl中类GLSurfaceView。GLSurfaceView的核心就是Renderer,初始化时会调用Renderer的onSurfaceCreated方法,每一帧的绘制是通过调用Renderer的onDrawFrame方法。Cocos2dxGLSurfaceView的Renderer是一个Cocos2dxRenderer对象,类Cocos2dxRenderer在

$(sourcedir)\cocos2dx\platform\android\java\src\org\cocos2dx\lib\Cocos2dxRenderer.java

中定义,其方法onSurfaceCreated代码如下:

public void onSurfaceCreated(final GL10 pGL10, final EGLConfig pEGLConfig) {
	Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight);
	this.mLastTickInNanoSeconds = System.nanoTime();
}

这里主要调用了一个方法nativeInit,nativeInit被声明为:

private static native void nativeInit(final int pWidth, final int pHeight);

即它是一个native的函数,即该函数用C++代码中实现,关于native函数简单说,就是在java在调用C++代码实现的函数,即需要采用JNI技术(可以看成是Java与C++交互的一个协议~)。方法nativeInit对应的C++实现是在(sourcedir)\samples\Lua\HelloLua\proj.android\jni\hellolua\main.cpp中:

void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)
{
    if (!CCDirector::sharedDirector()->getOpenGLView())
    {
        CCEGLView *view = CCEGLView::sharedOpenGLView();
        view->setFrameSize(w, h);

        AppDelegate *pAppDelegate = new AppDelegate();
        CCApplication::sharedApplication()->run();
    }
    else
    {
	.....
    }
}

这里的函数命名Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit是完全按照JNI实现方法的命名规则。CCDirector就是是cocos2d-x中的导演类,该类在$(sourcedir)\cocos2dx\CCDirector.cpp中实现。方法sharedDirector是类CCDirector的一个静态方法,该方法用来创建游戏中唯一的CCDirector对象,代码如下:

CCDirector* CCDirector::sharedDirector(void)
{
    if (!s_SharedDirector)
    {
        s_SharedDirector = new CCDisplayLinkDirector();
        s_SharedDirector->init();
    }

    return s_SharedDirector;
}

这里的CCCCDisplayLinkDirector是CCDirector的子类,方法init()做一些初始化工作:

bool CCDirector::init(void)
{
    setDefaultValues();

    //场景相关
    ...
    ...

    //管理场景的数组栈
    m_pobScenesStack = new CCArray();
    m_pobScenesStack->init();

    // 一些FPS等相关的成员变量初始化
    ...
    ...

    //初始化调度器对象
    m_pScheduler = new CCScheduler();

    //动作管理器对象
    m_pActionManager = new CCActionManager();
    m_pScheduler->scheduleUpdateForTarget(m_pActionManager, kCCPrioritySystem, false);

    // touchDispatcher,KeypadDispatcher,Accelerometer等对象初始化
    ...
    ...

    // CCPoolManager中实现了cocos2d-x CCObject对象的内存管理机制
    CCPoolManager::sharedPoolManager()->push();

    return true;
}

创建好导演对象后,Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit中调用了该对象的getOpenGLView方法:

inline CCEGLView* getOpenGLView(void) { return m_pobOpenGLView; }

该方法返回用于游戏绘制的CCEGLView,在init()中,m_pobOpenGLView复制为NULL。在Android平台下,这个CCEGLView其实没有什么作用,因为游戏都是绘制在Cocos2dxGLSurfaceView上的,因此方法Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit接着执行if分支:

CCEGLView *view = CCEGLView::sharedOpenGLView();
view->setFrameSize(w, h);

这里同样创建了游戏中类CCEGLView的唯一实例,类在$(sourcedir)\cocos2dx\platform\android\CCEGLView.cpp中实现。接着执行

AppDelegate *pAppDelegate = new AppDelegate();
CCApplication::sharedApplication()->run();

创建了一个类AppDelegate对象,类AppDelegate在$(sourcedir)\samples\Lua\HelloLua\Classes\AppDelegate.cpp中实现,类AppDelegate在各个平台之间共用的:

class  AppDelegate : private cocos2d::CCApplication
{
public:
    AppDelegate();
    virtual ~AppDelegate();
    virtual bool applicationDidFinishLaunching();

    virtual void applicationDidEnterBackground();

    virtual void applicationWillEnterForeground();
};

该类继承自CCApplication,注意类CCApplication是在$(sourcedir)\cocos2dx\platform\android\CCApplication.cpp中实现的,它的实现是与平台相关的。创建AppDelegate实例的工作实质就是实例化CCApplication,它的构造函数代码如下:

CCApplication::CCApplication()
{
    CCAssert(! sm_pSharedApplication, "");
    sm_pSharedApplication = this;
}

因此在实例化AppDelegate时,主要工作是用AppDelegate对象初始化全局变量sm_pSharedApplication。接着后面调用CCApplication::sharedApplication()->run(),相关代码如下:

CCApplication* CCApplication::sharedApplication()
{
    CCAssert(sm_pSharedApplication, "");
    return sm_pSharedApplication;
}
int CCApplication::run()
{
    // Initialize instance and cocos2d.
    if (! applicationDidFinishLaunching())
    {
        return 0;
    }

    return -1;
}

run中调用的虚函数applicationDidFinishLaunching实质上调用的是子类AppDelegate中的applicationDidFinishLaunching,代码如下:

bool AppDelegate::applicationDidFinishLaunching()
{
    //初始化director
    CCDirector *pDirector = CCDirector::sharedDirector();
    pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());

    CCEGLView::sharedOpenGLView()->setDesignResolutionSize(480, 320, kResolutionNoBorder);

    //显示FPS
    pDirector->setDisplayStats(true);

    // 设置FPS
    pDirector->setAnimationInterval(1.0 / 60);

    //创建Lua脚本引擎
    CCLuaEngine* pEngine = CCLuaEngine::defaultEngine();
    CCScriptEngineManager::sharedManager()->setScriptEngine(pEngine);

    //加载执行lua脚本
    std::string path = CCFileUtils::sharedFileUtils()->fullPathForFilename("hello.lua");
    pEngine->executeScriptFile(path.c_str());

    return true;
}

至此游戏启动完成,但是我们并没看到类似while循环的东东(btw,分析windows和Linux平台上cocos2d-x的启动流程可以清楚看到类似while循环东东~),这是因为在android平台下,主循环是由渲染线程发起的,通过不断调用render来驱动的,具体说就是调用Java类Cocos2dxRenderer中的onDrawFrame方法:

public void onDrawFrame(final GL10 gl) {
	Cocos2dxRenderer.nativeRender();
}

其唯一的工作是调用了nativeRender方法,该方法是native的,其对应的实现是:

JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender(JNIEnv* env) {
	cocos2d::CCDirector::sharedDirector()->mainLoop();
}

即执行导演对象中的方法mainLoop:

void CCDisplayLinkDirector::mainLoop(void)
{
    // 此变量决定程序是否结束
    if (m_bPurgeDirecotorInNextLoop)
    {
        m_bPurgeDirecotorInNextLoop = false;

        purgeDirector();
    }
    else if (! m_bInvalid)
     {
        //屏幕绘制,并做一些相应的逻辑处理
         drawScene();

         // 释放对象管理器中CCObject 对象
         CCPoolManager::sharedPoolManager()->pop();
     }
}

这个方法就是所有平台最后真正执行的循环体。

参考资料

http://blog.csdn.net/cjj7905150/article/details/22587021

http://www.dapps.net/dev/gamedev/cocos2d-x-dev-the-game-loop-4.html

http://blog.leafsoar.com/archives/2013/05-05.html

时间: 2024-10-03 01:14:43

Cocos2d-x程序在Android下的启动过程的相关文章

quick cocos2d x 手机(Android端)启动过程学习

简要学习下quick cocos2d x 在安卓端启动的过程. 首先需要了解一点:quick cocos2d x是依托于Android的activity和GLSurfaceView(继承自SurfaceView)的环境来显示quick层的游戏界面. (1)首先quick类的android游戏从AndroidManifest.xml文件指定的activity(假设AC)启动. (2)AC继承父类的Cocos2dxActivity. (3)调用静态初始化块,加载cocos2dx的动态库.也就是一些C

使用spring等框架的web程序在Tomcat下的启动顺序及思路理清

大牛请绕过,此文仅针对自己小白水平,对web程序的启动流程做个清晰的回顾. 一.使用spring等框架的web程序在Tomcat下的启动流程 1)Tomcat是根据web.xml来启动的.首先到web.xml 2)web.xml中负责启动spring和spring mvc.对应的启动配置文件分别是 启动spring mvc,并进行所有资源路径映射 <servlet> <servlet-name>springMVC</servlet-name> <servlet-c

Android系统的启动过程

当我们拿到一台Android的智能手机,从打开开关,到我们可以使用其中的app时,这个启动过程到底是怎么样的? 系统上电 当给Android系统上电,CPU复位之后,程序指针会指向启动地址,从该地址读取启动程序的可执行代码直接运行,或者将可执行代码与数据载入CPU内置的RAM中再运行. CPU复位,其实就是在电源接通的瞬间,CPU内的寄存器和各引脚均会被置为初始状态,并将程序指针指向引导程序的位置. 这一段代码,放在PC中,叫做BIOS,而在Android等嵌入式系统中就叫做Bootloader

Android内核开发:图解Android系统的启动过程

本文是<Android内核开发>系列的第六篇文章,前面的几篇文章介绍了Android内核开发相关的基础知识,包括:Android源码的下载.版本和分支介绍.编译和烧写等等,从本文起就要开始真正地进行Android内核的学习及实战了. 学习任何软硬件系统,研究系统启动过程都是一种非常有效地起步手段,搞Android内核开发也不例外.网上有很多文章对Android启动相关代码进行分析和走读,大家可以先搜索阅读一下,我个人更喜欢更加直观的方式去理解未知的东西,包括图.表.系统输出的log信息等等,因

Android系统默认Home应用程序(Launcher)的启动过程源代码分析

在前面一篇文章中,我们分析了Android系统在启动时安装应用程序的过程,这些应用程序安装好之后,还需要有一个 Home应用程序来负责把它们在桌面上展示出来,在Android系统中,这个默认的Home应用程序就是Launcher了,本文将详细分析 Launcher应用程序的启动过程. Android系统的Home应用程序Launcher是由ActivityManagerService启动的,而 ActivityManagerService和PackageManagerService一样,都是在开

Android应用程序组件Content Provider的启动过程源代码分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6963418 通过前面的学习,我们知道在Android系统中,Content Provider可以为不同的应用程序访问相同的数据提供统一的入口.Content Provider一般是运行在独立的进程中的,每一个Content Provider在系统中只有一个实例存在,其它应用程序首先要找到这个实例,然后才能访问它的数据.那么,系统中的Conten

Android Phone进程启动过程详解

之前解决一个开机搜网慢的问题时,发现由于Phone进程起来以后才会主动连接RILD,因而在一定程度上Phone进程启动的时间会影响网络状态注册的快慢.适当的将Phone进程提前,可以将网络注册时间提前一点,让状态栏中信号显示的时间提前.那么,Android中作为系统的核心进程之一,Phone进程是如何启动的了? RIL运行机制请参考: http://blog.csdn.net/jason_wzn/article/details/53232022 Telephony最开始创建的是PhoneFact

Android系统进程Zygote启动过程的源代码分析

原文地址:http://blog.csdn.net/luoshengyang/article/details/6747696 Android应用程序框架层创建的应用程序进程具有两个特点,一是进程的入口函数是ActivityThread.main,二是进程天然支持Binder进程间通信机制:这两个特点都是在进程的初始化过程中实现的,本文将详细分析Android应用程序进程创建过程中是如何实现这两个特点的. Android应用程序框架层创建的应用程序进程的入口函数是ActivityThread.ma

android 性能优化 -- 启动过程 冷启动 热启动

一.应用的启动方式 通常来说,启动方式分为两种:冷启动和热启动. 1.冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动. 2.热启动:当启动应用时,后台已有该应用的进程(例:按back键.home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动. 特点 1.冷启动:冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化