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就会继续游戏主线程逻辑,否则跳过。
总结:
-
GameActivity成为了Java层与C++层标准化桥梁,提供了一套对接方法。
-
Cocos游戏主线程会受GameActivity生命回调暂停或恢复主线程逻辑。
-
大部分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。
从上图可以得出:
-
Surface是由WMS管理,Activity进入前台则会获取,反之Activity进入后台则会被释放。
-
Cocos引擎根据surface是否有效暂停或恢复主线程逻辑。