Cocos Engine对接AGDK

文章详细描述了Cocos游戏如何通过JNI在Java和C++层间传递生命周期事件,以及GameActivity在Android应用中的角色,特别是如何通过AndroidNativeAppGlue管理和同步Java和C++层的逻辑,确保游戏主线程与Activity的交互和Surface的管理。
摘要由CSDN通过智能技术生成

Cocos接入Android Game Development Kit(GameActivity)

        Java层GameActivity将Stop生命周期回调通过JNI调用到C++层GameActivity的onNativeStop到android_native_app_glue里的onPause,android_native_app_glue通过Pipe管道将APP_CMD_STOP事件从主线程切换到游戏的主线程,将事件传递给了AndroidPlatform,并且AndroidPlatform则把_isVisible修改成false。

        每当AndroidPlatform的一层循环调用的时候会检查_isVisible && _hasWindow状态,如果TRUE就会继续游戏主线程逻辑,否则跳过。

总结:

  1. GameActivity成为了Java层与C++层标准化桥梁,提供了一套对接方法。

  2. Cocos游戏主线程会受GameActivity生命回调暂停或恢复主线程逻辑。

  3. 大部分Android App是以多个Activity作为页面栈进行管理,CocosActivity自然会因为生命周期调用被暂停。

JNI调用流程

Cocos应用选用了android_native_app_glue框架进行开发,需要在Native和Java两端分别实现部分功能。

1.Java侧

上层需要实现的较为简单。仅需要创建一个继承于GameActivity的CocosActivity,并在Manifest.xml中将其与SDK中的某一动态库进行绑定即可。

(1)创建Activity

 
public class AppActivity extends CocosActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // DO OTHER INITIALIZATION BELOW
        SDKWrapper.shared().init(this);

    }
    ....

(2)绑定

 
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
  <uses-permission android:name="android.permission.INTERNET"/>
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
  <application android:extractNativeLibs="true" android:allowBackup="true" android:label="@string/app_name" android:usesCleartextTraffic="true" android:icon="@mipmap/ic_launcher" android:resizeableActivity="true">
    <meta-data android:name="android.app.lib_name" android:value="cocos"/>
    <activity android:name="com.cocos.game.AppActivity" android:screenOrientation="portrait" android:configChanges="orientation|keyboardHidden|screenSize|screenLayout|smallestScreenSize" android:label="@string/app_name" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:launchMode="singleTask" android:exported="true">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
    </activity>
    <activity android:name="com.cocos.lib.CocosEditBoxActivity" android:configChanges="orientation|keyboardHidden|screenSize|screenLayout|smallestScreenSize" android:screenOrientation="behind" android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"/>
  </application>
</manifest>

其中,android.app.lib_name指定了sdk中动态库的名称。

2.Native侧

(1)实现extern "C" void android_main(struct android_app* state)方法

在cpp文件中实现该方法,Cocos Engine在JniCocosEntry.cpp中实现。android_main的大致框架如下。

void android_main(struct android_app *app) {
    auto *platform = cc::BasePlatform::getPlatform();
    auto *androidPlatform = static_cast<cc::AndroidPlatform *>(platform);
    androidPlatform->setAndroidApp(app);
    androidPlatform->init();
    androidPlatform->run(0, nullptr);
}

这里_app->onAppCmd = handleCmdProxy;应该是指定cmd处理方法

int AndroidPlatform::init() {
#if CC_USE_XR
    registerInterface(std::make_shared<XRInterface>());
#endif
    IXRInterface *xr = CC_GET_XR_INTERFACE();
    if (xr) {
        JniHelper::getEnv();
        xr->initialize(JniHelper::getJavaVM(), getActivity());
    }
    cc::FileUtilsAndroid::setAssetManager(_app->activity->assetManager);
    _inputProxy = ccnew GameInputProxy(this);
    _inputProxy->registerAppEventCallback([this](int32_t cmd) {
        IXRInterface *xr = CC_GET_XR_INTERFACE();
        if (xr) {
            xr->handleAppCommand(cmd);
        }

        if (APP_CMD_START == cmd || APP_CMD_INIT_WINDOW == cmd) {
            if (_inputProxy->isAnimating()) {
                _isLowFrequencyLoopEnabled = false;
                _loopTimeOut = 0;
            }
        } else if (APP_CMD_STOP == cmd) {
            _lowFrequencyTimer.reset();
            _loopTimeOut = LOW_FREQUENCY_TIME_INTERVAL;
            _isLowFrequencyLoopEnabled = true;
            if (xr && !xr->getXRConfig(xr::XRConfigKey::INSTANCE_CREATED).getBool()) {
                // xr will sleep,  -1 we will block forever waiting for events.
                _loopTimeOut = -1;
                _isLowFrequencyLoopEnabled = false;
            }
        }
    });
    _app->userData = _inputProxy;
    _app->onAppCmd = handleCmdProxy;

    registerInterface(std::make_shared<Accelerometer>());
    registerInterface(std::make_shared<Battery>());
    registerInterface(std::make_shared<Network>());
    registerInterface(std::make_shared<Screen>());
    registerInterface(std::make_shared<System>());
    registerInterface(std::make_shared<SystemWindowManager>());
    registerInterface(std::make_shared<Vibrator>());

    return 0;
}
....

static void handleCmdProxy(struct android_app *app, int32_t cmd) {
    auto *proxy = static_cast<GameInputProxy *>(app->userData);
    proxy->handleAppCommand(cmd);
}

cmd在这里被处理

 
    void handleAppCommand(int32_t cmd) {
        switch (cmd) {
            case APP_CMD_SAVE_STATE:
                // The system has asked us to save our current state.
                CC_LOG_DEBUG("AndroidPlatform: APP_CMD_SAVE_STATE");
                break;
            case APP_CMD_INIT_WINDOW: {
                _hasWindow = true;
                ANativeWindow *nativeWindow = _androidPlatform->_app->window;

                // We have a window!
                CC_LOG_DEBUG("AndroidPlatform: APP_CMD_INIT_WINDOW");
                if (!_launched) {
                    _launched = true;

                    ISystemWindowInfo info;
                    info.width = ANativeWindow_getWidth(nativeWindow);
                    info.height = ANativeWindow_getHeight(nativeWindow);
                    info.externalHandle = nativeWindow;
                    _androidPlatform->getInterface<SystemWindowManager>()->createWindow(info);

                    if (cocos_main(0, nullptr) != 0) {
                        CC_LOG_ERROR("AndroidPlatform: Launch game failed!");
                    } else {
                        IXRInterface *xr = CC_GET_XR_INTERFACE();
                        if (xr) {
                            xr->onRenderResume();
                        }
                    }
                } else {
                    IXRInterface *xr = CC_GET_XR_INTERFACE();
                    if (xr) {
                        xr->onRenderResume();
                    }

                    auto *windowMgr = _androidPlatform->getInterface<SystemWindowManager>();
                    auto *window = static_cast<cc::SystemWindow *>(windowMgr->getWindow(ISystemWindow::mainWindowId));
                    window->setWindowHandle(nativeWindow);
                    events::WindowRecreated::broadcast(ISystemWindow::mainWindowId);
                }
                break;
            }
            case APP_CMD_TERM_WINDOW: {
                _hasWindow = false;
                // The window is going away -- kill the surface
                CC_LOG_DEBUG("AndroidPlatform: APP_CMD_TERM_WINDOW");
                IXRInterface *xr = CC_GET_XR_INTERFACE();
                if (xr) {
                    xr->onRenderPause();
                }
                // NOLINTNEXTLINE
                events::WindowDestroy::broadcast(ISystemWindow::mainWindowId);
                break;
            }
            .....

Cocos是如何获得Surface

        从上图可以看得到Cocos利用GameActivity同步Java层生命周期调用,并且根据_isVisible && _hasWindow状态,onStop控制_isVisible,那_hasWindow是又是被谁控制呢?

        ViewRootImpl被调用performTraversals之后通过mWindowSession请求WMS(WindowManagerService)对Window进行relayout,当Native的Surface真正被创建之后,ViewRootImpl调用notifySurfaceCreated,将回调调用到Cocos的SurfaceView,然后SurfaceView才调用到注册了SurfaceHolder的GameActivity。

从上图可以得出:

  1. Surface是由WMS管理,Activity进入前台则会获取,反之Activity进入后台则会被释放。

  2. Cocos引擎根据surface是否有效暂停或恢复主线程逻辑。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值