我们开发了一个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. Nginx系列教程(一)| 手把手教你在Linux环境下搭建Nginx服务
  2. Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
  3. 50款Android手机必备应用大全
  4. android NDK安装
  5. GDB+gdbserver 远程调试android native code
  6. Android(安卓)中Activity,Window和View之间的关系
  7. Android签名教程
  8. Android应用程序整合第三方API(以高德Map API为例)
  9. Android(安卓)Studio3.4 中无法找到Android(安卓)Device Monitor

随机推荐

  1. android File保存数据
  2. Android 将被依赖的库项目打包成aar(非jar
  3. Android上使用ASIFT实现对视角变化更鲁棒
  4. 「Android」Huawei U8825d new Lowmemory
  5. Android 6.0 (marshmallow)中新的新技术
  6. ()Android中的Activity创建与周期
  7. Android上安装并运行 Ubuntu 12.04
  8. 关于Android中四类核心组件的简介
  9. Android中经常用到的方法--SDcard下文件
  10. Android开发周报:Android将使用OpenJDK、R