app passed NULL surface 问题解决

欢迎访问个人博客:https://glumes.com

在进行安卓相机开发时,遇到一个 app passed NULL surface 的问题。

主要就是当拍摄完一张照片后,跳转到另一个 Activity 去显示拍的图像,然后再返回到拍摄界面时就出现了崩溃闪退。

app passed NULL surface 问题解决_第1张图片
app passed NULL surface

后来经过排查,才发现是我在 Activity 的生命周期中没有处理好 Camera 对应的状态。

Activity 的生命周期从 onCreate 到 onStart 再到 onResume。

这时如果跳转到其他的 Activity 后再回来,则是先经过 onPause,再到 onResume 。

而对相机的设置也主要是在这个两个状态里面进行的。

假如使用 TextureView 来预览相机内容,那么就不要在 onCreate 里面给 TextureView 设置了回调接口 TextureView.SurfaceTextureListener ,而是要在 onResume 里面进行。该接口的主要作用就是当 TextureView 准备好了之后,就去打开相机。

onResume 中的代码如下:

        if (textureView.isAvailable()) {
            openCamera();
        } else {
            textureView.setSurfaceTextureListener(surfaceTextureListener);
        }

在谷歌的 Sample 样例给出了注释说明:

 // When the screen is turned off and turned back on, the SurfaceTexture is already
        // available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
        // a camera and start preview from here (otherwise, we wait until the surface is ready in
        // the SurfaceTextureListener).

因为当屏幕关闭或者返回时,TextureView 的 SurfaceTexture 已经是可用的了,onSurfaceTextureAvailable 的回调接口就不会被调用,这样就不能去打开相机进行预览了,所以我们要进一个判断才行。

而在 onPause 状态中,要做一些释放的工作,关闭 Camera 之类的。

    @Override
    protected void onPause() {
        super.onPause();
        closeCamera();
        stopBackgroundThread();
    }

    private void closeCamera() {
        if (cameraCaptureSessions != null) {
            cameraCaptureSessions.close();
        }
        if (null != mCameraDevice) {
            mCameraDevice.close();
            mCameraDevice = null;
        }
        if (null != mImageReader) {
            mImageReader.close();
            mImageReader = null;
        }
    }

  protected void stopBackgroundThread() {
        mBackgroundThread.quitSafely();
        try {
            mBackgroundThread.join();
            mBackgroundThread = null;
            mBackgroundHandler = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

要注意是,cameraCaptureSessions 的关闭要在 mBackgroundThread 之前,否则回报的,虽然不会导致 Crash。

然而,当我照上面那样处理生命周期状态时,依旧会报 app passed NULL surface 的问题。

最后发现是由于 ImageReader 的处理不当导致的。

在 onCreate 状态时去初始化了 ImageReader ,然后再到 onResume 里面去设置回调打开 Camera 之类的。

打开 Cemera 进行预览,就要创建 CaptureSession,要传递 Surface 作为参数,这个时候就要传递 ImageReader.getSurface() 的作为参数之一。

而报错的内容也就是 ImageReader.getSurface 的为 null 了。

当跳转之后再回到当前 Activity,ImageReader 的 Surface 就已经为 NULL 了。

ImageReader.getSurface() 的方法有一段注解如下:

     * 

Please note that holding on to the Surface object returned by this method is not enough * to keep its parent ImageReader from being reclaimed. In that sense, a Surface acts like a * {@link java.lang.ref.WeakReference weak reference} to the ImageReader that provides it.

* * @return A {@link Surface} to use for a drawing target for various APIs.

也就是说 Surface 相当于是 ImageReader 的一个弱引用。

所以,要避免 app passed NULL surface 的问题出现,就应该在 onResume 里面去初始化 ImageReader ,最好是在 打开相机 openCamera 之前去初始化 ImageReader。

一起交流学习,答疑解惑,有问题,我们星球见~~~


app passed NULL surface 问题解决_第2张图片
图形/图像/音视频交流

最后,如果觉得文章不错,欢迎关注微信公众号:纸上浅谈


app passed NULL surface 问题解决_第3张图片
纸上浅谈

你可能感兴趣的:(app passed NULL surface 问题解决)