复现步骤:
1.安装好apk
2.启动app -> Splash页面 -> 申请授权弹窗
3.同意或者拒接 -> 退出弹窗 -> 屏幕闪烁
Log:
运行中出现GL Error 0x0506
初步排查发现导致该问题的原因是CCDirector::mainLoop()
函数中 _invalid
标识为true
导致未执行 drawScene()
方法:
void Director::mainLoop()
{
if (_purgeDirectorInNextLoop)
{
_purgeDirectorInNextLoop = false;
purgeDirector();
}
else if (_restartDirectorInNextLoop)
{
_restartDirectorInNextLoop = false;
restartDirector();
}
else if (! _invalid)
{
drawScene();
// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}
排查为什么_invalid
标识会变为true
,发现修改该变量的地方为:
void Director::startAnimation()
{
_invalid = false;
}
void Director::stopAnimation()
{
_invalid = true;
}
继续往下!
// AppDelegate.cpp
void AppDelegate::applicationDidEnterBackground()
{
Director::getInstance()->stopAnimation();
}
void AppDelegate::applicationWillEnterForeground()
{
Director::getInstance()->startAnimation();
}
// Java_org_cocos2dx_lib_Cocos2dxRenderer.cpp
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnResume() {
static bool firstTime = true;
if (Director::getInstance()->getOpenGLView()) {
// don't invoke at first to keep the same logic as iOS
// can refer to https://github.com/cocos2d/cocos2d-x/issues/14206
if (!firstTime)
Application::getInstance()->applicationWillEnterForeground();
firstTime = false;
}
}
JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnPause() {
if (Director::getInstance()->getOpenGLView()) {
Application::getInstance()->applicationDidEnterBackground();
}
}
// Cocos2dxRenderer.java
public void handleOnPause() {
Cocos2dxRenderer.nativeOnPause();
}
public void handleOnResume() {
Cocos2dxRenderer.nativeOnResume();
}
// Cocos2dxGLSurfaceView.java
@Override
public void onResume() {
Log.d(TAG, "onResume()");
super.onResume();
this.setRenderMode(RENDERMODE_CONTINUOUSLY);
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleOnResume();
}
});
}
@Override
public void onPause() {
Log.d(TAG, "onPause()");
this.queueEvent(new Runnable() {
@Override
public void run() {
Cocos2dxGLSurfaceView.this.mCocos2dxRenderer.handleOnPause();
}
});
this.setRenderMode(RENDERMODE_WHEN_DIRTY);
// super.onPause();
}
由此我们可以得出结论,是因为授权弹窗弹出触发Cocos2dxGLSurfaceView.onPause()
方法,导致_invalid
为true
那么问题来了,为什么弹框消失的时候触发的Cocos2dxGLSurfaceView.onResume()
不会把_invalid
还原为false
呢?
根据流程我发现Cocos2d引擎源码在Java_org_cocos2dx_lib_Cocos2dxRenderer.java
的JNI函数JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnResume()
方法里设置了一个静态变量static bool firstTime = true;
当第一次执行该函数的时候会跳过执行applicationWillEnterForeground()
方法,那么我们接下来只需要判断弹框消失后触发的Cocos2dxGLSurfaceView.onResume()
是不是程序运行的第一次调用,而测试结果确实如此!!!
到此,我们定位到了该问题的产生逻辑,那么接下来如何解决这个问题就简单多了。
解决思路选择其中之一即可:
1.在JNI方法JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeOnResume()
中做特殊处理:
定义布尔类型类成员变量bool isPause = false
,当执行nativeOnPause()
时把isPause
置为true
,再添加判断条件
if (!firstTime || isPause)
2.在Cocos2dxActivity
中处理:
private void resumeIfHasFocus() {
if(hasFocus) { // 取消hasFocus判断条件
this.hideVirtualButton();
Cocos2dxHelper.onResume();
mGLSurfaceView.onResume();
}
}
当然还有其他解决思路,同时,不同的项目上述的解决方案可能会导致一些其他问题,需要根据具体项目测试。