CocosCreator Andorid工程的啓動過程

寫在前面

本篇內容主要是關於用cocoscreator構建的Android工程,在運行apk時候的啓動流程。
CocosCreator版本:2.3.1

正文

1、主Activity入口在AndroidMainifest.xml中定義

<activity>
            android:name="org.cocos2dx.javascript.AppActivity"
            ...
</activity>            

2、Cocos2dxActivity的onCreate()方法

AppActivity繼承Cocos2dxActivity,在它的onCreate()方法並沒有處理太多東西,而是調用了父類的onCreate()方法。代碼如下:

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        Log.d(TAG, "Cocos2dxActivity onCreate: " + this + ", savedInstanceState: " + savedInstanceState);
        super.onCreate(savedInstanceState);

        // Workaround in https://stackoverflow.com/questions/16283079/re-launch-of-activity-on-home-button-but-only-the-first-time/16447508
        if (!isTaskRoot()) {
            // Android launched another instance of the root activity into an existing task
            //  so just quietly finish and go away, dropping the user back into the activity
            //  at the top of the stack (ie: the last state of this task)
            finish();
            Log.w(TAG, "[Workaround] Ignore the activity started from icon!");
            return;
        }

        Utils.setActivity(this);

        Utils.hideVirtualButton();

        Cocos2dxHelper.registerBatteryLevelReceiver(this);

        // 把工程中libs下面的so文件load進來,定義在AndroidManifest, meta-data標籤下,android.app.lib_name. 最終在包的data/data/com.XXX.XXX/lib下面
        onLoadNativeLibraries();

        sContext = this;
        this.mHandler = new Cocos2dxHandler(this);  // 處理安卓的彈窗等
        
        Cocos2dxHelper.init(this);
        CanvasRenderingContext2DImpl.init(this);
        
        this.mGLContextAttrs = getGLContextAttrs(); // 獲取OpenGLES相關屬性
        this.init(); // 初始化

        if (mVideoHelper == null) {
            mVideoHelper = new Cocos2dxVideoHelper(this, mFrameLayout);
        }

        if(mWebViewHelper == null){
            mWebViewHelper = new Cocos2dxWebViewHelper(mFrameLayout);
        }

        Window window = this.getWindow();
        window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
        this.setVolumeControlStream(AudioManager.STREAM_MUSIC);
    }
    protected void onLoadNativeLibraries() {
        try {
            ApplicationInfo ai = getPackageManager().getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);
            Bundle bundle = ai.metaData;
            // 獲取在AndroidManifest.xml中定義.so文件名,並調用System.loadLibrary加載
            String libName = bundle.getString("android.app.lib_name");
            System.loadLibrary(libName);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

在AndroidManifest.xml的定義:

        <!-- Tell Cocos2dxActivity the name of our .so -->
        <meta-data android:name="android.app.lib_name"
                   android:value="cocos2djs" />

3、Cocos2dxActivity的init()方法

    public void init() {
        ViewGroup.LayoutParams frameLayoutParams =
                new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                                           ViewGroup.LayoutParams.MATCH_PARENT);
        mFrameLayout = new FrameLayout(this);
        mFrameLayout.setLayoutParams(frameLayoutParams);

        // 添加一個surfaceView,surfaceView是什麼,下面講
        Cocos2dxRenderer renderer = this.addSurfaceView();
        this.addDebugInfo(renderer);

        // Should create EditBox after adding SurfaceView, or EditBox will be hidden by SurfaceView.
        mEditBox = new Cocos2dxEditBox(this, mFrameLayout);

        // Set frame layout as the content view
        setContentView(mFrameLayout);
    }

addSurfaceView():

    private Cocos2dxRenderer addSurfaceView() {
        // 創建一個surfaceView
        this.mGLSurfaceView = this.onCreateView();
        this.mGLSurfaceView.setPreserveEGLContextOnPause(true);
        // Should set to transparent, or it will hide EditText
        // https://stackoverflow.com/questions/2978290/androids-edittext-is-hidden-when-the-virtual-keyboard-is-shown-and-a-surfacevie
        mGLSurfaceView.setBackgroundColor(Color.TRANSPARENT);
        // Switch to supported OpenGL (ARGB888) mode on emulator
        if (isAndroidEmulator())
            this.mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);

        // 註冊自主實現的渲染器,渲染器又用來幹嘛的,下面講
        Cocos2dxRenderer renderer = new Cocos2dxRenderer();
        this.mGLSurfaceView.setCocos2dxRenderer(renderer);

        // 將新建的surfaceView添加到mFrameLayout中
        mFrameLayout.addView(this.mGLSurfaceView);

        return renderer;
    }
    
    public Cocos2dxGLSurfaceView onCreateView() {
    	// Cocos2dxGLSurfaceView 繼承於 android.opengl.GLSurfaceView
        Cocos2dxGLSurfaceView glSurfaceView = new Cocos2dxGLSurfaceView(this);
        //this line is need on some device if we specify an alpha bits
        if(this.mGLContextAttrs[3] > 0) glSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);

        Cocos2dxEGLConfigChooser chooser = new Cocos2dxEGLConfigChooser(this.mGLContextAttrs);
        glSurfaceView.setEGLConfigChooser(chooser);

        return glSurfaceView;
    }

4、GLSurfaceView和GLSurfaceView.Render

Android提供了兩個基本的類GLSurfaceViewGLSurfaceView.Render來讓我們使用OpenGL ES API來創建和操縱圖形。

GLSurfaceView

它是一個視圖類,你可以調用OpenGL API在上面繪製圖形和操縱物體,功能和SurfaceView相似。其中Cocos2dxGLSurfaceView 就是繼承這個類,並且擴展這個類重寫onTouchEvent、onKeyDown、onKeyUp等函數。

GLSurfaceView.Render

這個接口定義了在一個OpenGL的GLSurfaceView中繪製圖形所需要的的方法,我們必須在一個單獨的類中爲這些接口提供實現,並使用GLSurfaceView.setRenderer()方法將它依附到GLSurfaceView實例對象上。Cocos2dxRenderer就是繼承此類。
我們需要實現GLSurfaceView.Render的以下方法:

  • onSurfaceCreated():系統在創建GLSurfaceView時調用它一次。我們可以使用它來設置OpenGL的環境變量,或是初始化OpenGL的圖形物體。
  • onSurfaceChanged():系統在GLSurfaceView的幾何屬性發生改變時調用該方法,包括大小或是設備屏幕的方向發生變化。例如,系統在屏幕從直立變爲水平使調用它。這個方法主要用來對GLSurfaceView容器的變化進行響應。
  • onDrawFrame():系統在每次重繪GLSurfaceView時調用這個方法。這個方法主要完成繪製圖形的操作。

接下來繼續看Cocos2dxRenderer類

    @Override
    public void onSurfaceCreated(final GL10 GL10, final EGLConfig EGLConfig) {
        mNativeInitCompleted = false;
        // 這裏調用了一個定義爲native的函數,它是通過jni來訪問c++實現的方法,具體下面講
        Cocos2dxRenderer.nativeInit(this.mScreenWidth, this.mScreenHeight, mDefaultResourcePath);
        mOldNanoTime = System.nanoTime();
        this.mLastTickInNanoSeconds = System.nanoTime();
        mNativeInitCompleted = true;
        if (mGameEngineInitializedListener != null) {
            Cocos2dxHelper.getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mGameEngineInitializedListener.onGameEngineInitialized();
                }
            });
        }
    }
    @Override
    public void onSurfaceChanged(final GL10 GL10, final int width, final int height) {
        // 同樣的調用了c++的方法
        Cocos2dxRenderer.nativeOnSurfaceChanged(width, height);
    }
    @Override
    public void onDrawFrame(final GL10 gl) {
        if (mNeedToPause)
            return;

        if (mNeedShowFPS) {
            /////////////////////////////////////////////////////////////////////
            //IDEA: show FPS in Android Text control rather than outputing log.
            ++mFrameCount;
            long nowFpsTime = System.nanoTime();
            long fpsTimeInterval = nowFpsTime - mOldNanoTime;
            if (fpsTimeInterval > 1000000000L) {
                double frameRate = 1000000000.0 * mFrameCount / fpsTimeInterval;
                Cocos2dxHelper.OnGameInfoUpdatedListener listener = Cocos2dxHelper.getOnGameInfoUpdatedListener();
                if (listener != null) {
                    listener.onFPSUpdated((float) frameRate);
                }
                mFrameCount = 0;
                mOldNanoTime = System.nanoTime();
            }
            /////////////////////////////////////////////////////////////////////
        }
        /*
         * No need to use algorithm in default(60 FPS) situation,
         * since onDrawFrame() was called by system 60 times per second by default.
         */
        if (sAnimationInterval <= INTERVAL_60_FPS) {
            // 同樣的調用了c++的方法
            Cocos2dxRenderer.nativeRender();
        } else {
            final long now = System.nanoTime();
            final long interval = now - this.mLastTickInNanoSeconds;

            if (interval < Cocos2dxRenderer.sAnimationInterval) {
                try {
                    Thread.sleep((Cocos2dxRenderer.sAnimationInterval - interval) / Cocos2dxRenderer.NANOSECONDSPERMICROSECOND);
                } catch (final Exception e) {
                }
            }
            /*
             * Render time MUST be counted in, or the FPS will slower than appointed.
            */
            this.mLastTickInNanoSeconds = System.nanoTime();
            Cocos2dxRenderer.nativeRender();
        }
    }

5、c++中實現的nativeInit函數

可以看出,上述說的三個方法關鍵的地方就是分別調用了nativeInitnativeOnSurfaceChangednativeRender,這些方法都是在C++定義和實現的,這些方法的實現放在frameworks/cocos2d-x/cocos/platform/android/jni/JniImp.cpp中。

    JNIEXPORT void JNICALL JNI_RENDER(nativeInit)(JNIEnv*  env, jobject thiz, jint w, jint h, jstring jDefaultResourcePath)
    {
        g_width = w;
        g_height = h;
        
        // 這個方法new了一個AppDelegate
        // auto app = new AppDelegate(width, height);
        g_app = cocos_android_app_init(env, w, h);

        g_isGameFinished = false;
        ccInvalidateStateCache();
        std::string defaultResourcePath = JniHelper::jstring2string(jDefaultResourcePath);
        LOGD("nativeInit: %d, %d, %s", w, h, defaultResourcePath.c_str());
        

        if (!defaultResourcePath.empty())
            FileUtils::getInstance()->setDefaultResourceRootPath(defaultResourcePath);

        se::ScriptEngine* se = se::ScriptEngine::getInstance();
        se->addRegisterCallback(setCanvasCallback);

        EventDispatcher::init();

		// 程序開始運行,android的Application實現放在CCApplication-android.cpp中,start的代碼如下:
        g_app->start();
        g_isStarted = true;
    }
void Application::start()
{
	// applicationDidFinishLaunching具體的實現在frameworks/runtime-src/Classes/AppDelegate.cpp中
    if(!applicationDidFinishLaunching())
        return;
}

bool AppDelegate::applicationDidFinishLaunching()
{
    se::ScriptEngine* se = se::ScriptEngine::getInstance();

    jsb_set_xxtea_key("65dd8463-fe52-43");
    jsb_init_file_operation_delegate();

#if defined(COCOS2D_DEBUG) && (COCOS2D_DEBUG > 0)
    // Enable debugger here
    jsb_enable_debugger("0.0.0.0", 6086, false);
#endif

    se->setExceptionCallback([](const char* location, const char* message, const char* stack){
        // Send exception information to server like Tencent Bugly.

    });

    jsb_register_all_modules();

    se->start();

    se::AutoHandleScope hs;
    // 通過jsb調用js腳本,main.js是入口腳本
    jsb_run_script("jsb-adapter/jsb-builtin.js");
    jsb_run_script("main.js");

    se->addAfterCleanupHook([](){
        JSBClassType::destroy();
    });

    return true;
}

以上就是CocosCreator2.3.1版本構建的Android工程啓動的流程,如果有哪裏不對的歡迎指出!

參考鏈接:
https://www.cnblogs.com/Monte/p/6735061.html
https://www.jb51.net/article/96074.htm

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章