安卓cocos2dx游戏启动时授权弹窗申请导致的闪屏

复现步骤:
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()方法,导致_invalidtrue

那么问题来了,为什么弹框消失的时候触发的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();
    }
}

当然还有其他解决思路,同时,不同的项目上述的解决方案可能会导致一些其他问题,需要根据具体项目测试。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容