Android Native OpenGL应用前后台切换的总结

我们开发了一个Android的3D应用,界面部分使用Java,渲染部分使用了C++,但发布后,应用在前后台切换时总是再渲染功能上出现错误导致程序崩溃,经过几天的奋战,终于整清楚了。

Android在进行前后台切换时,如果程序中不显示调用View的onPause和onResume,那么系统不会自动调用其onPause和onResume,在我们的程序中,在activity的onPause和onResume分别调用view相应的函数。

查看GLSurfaceView的源码发现Android在进行前后台切换时,自动重新创建EGLSurface和EGLContext,即在suspend(onPause)时删除现有的对象,然后在resume时再创建新的对象,虽然有一个属性setPreserveEGLContextOnPause,但不能保证百分百好用,与具体手机有关,根据我们测试的情况是,如果setPreserveEGLContextOnPause(true),那么程序不会调用Renderer的onSurfaceCreated,但会发生EGL_CONTEXT_LOST,
从而导致EGLSurface和EGLContext被重建(虽然重建了,但仍然不会调用onSurfaceCreated),因此为了能满足所有情况,
还是setPreserveEGLContextOnPause(false)。

根据GLSurfaceView中的文档描述,渲染环境重建时,会自动删除OpenGL的相关资源,因此不需要我们手动调用释放资源的动作。

我们的应用要适应现有的情况,则必须:
1.在渲染环境删除前保存一些运行时资源,
2.然后在渲染环境重建后,第一时间恢复这些运行时资源,并把在前一个渲染环境中创建的OpenGL对象置为初始状态(只需要置状态,不需要调用OpenGL的删除资源函数, 注意:一定不能调用删除函数,否则后续渲染就会出现未知的效果错误,比如花屏,贴图缺失等奇异错误)

对于恢复的时机比较容易找到,那就是onSurfaceCreated,但保存运行资源的位置却没有明显的时机,首先调用保存动作,必须在渲染环境环境删除前,而且必须是在渲染线程中执行,我们初步确定了几个位置onPause,EGLWindowSurfaceFactory.destroySurface,EGLContextFactory.destroyContext,我们都试了,但居然不好使,始终提示设备环境无效,经过一番努力终于搞定,在onPause中调用,具体代码如下:
@Override
public void onPause() {
        queueEvent(new Runnable() {
                @Override
                public void run() {
                    //保存运行时资源
...
                }
            });
    super.onPause();
    }

就是必须在GLSurfaceView的onPause之前调用自己的代码,在activity中也是类似,在super.onPause()前调用view的onPause, activity的onResume正好相反,在super.onResume之后调用view的onResume.

发布程序,终于好用了!

有个小插曲,根据我的经验一般重建渲染环境,需要重建窗口,但android在没有重建窗口的情况下居然也能重建渲染环境,让我感到很诧异,后来查看ndk的程序
ANativeActivity的callbacks有一个回调onNativeWindowCreated,在程序从后台切换到前台时会调用onNativeWindowCreated,说明符合之前的经验,
而我们在程序中保存了ANativeWindow对象的指针,因此在程序从后台切换到前台时,也需要重新获取ANativeWindow指针,否则会出错:
在我们程序中唯一使用ANativeWindow的地方就是取得窗口尺寸:
h = ANativeWindow_getHeight(win_handle_);
w = ANativeWindow_getWidth(win_handle_);

如果不重新获取这个指针,w和h得到的都是负数,我们的程序前端接的GLSurfaceView,从GLSurfaceView中无法获得ANativeWindow指针,必须先在JAVA程序中
取得Surface对象:
Surface surf = GLSurfaceView的对象.getHolder().getSurface();
然后在C++中从Surface获取ANativeWindow指针:
ANativeWindow* window = ANativeWindow_fromSurface(env, surface);

这个程序切到前台时也必须刷新ANativeWindow指针,才能取到正确的窗口尺寸。
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值