【问题标题】:CameraActivity life Cycle camera release throwing errorCameraActivity生命周期相机释放抛出错误
【发布时间】:2017-09-19 19:33:22
【问题描述】:

我正在使用表面视图和 Camera 1 API 创建一个自定义相机应用程序,生命周期中存在某种问题,我无法让它工作,但基本上我正在释放相机,然后再次调用它:

在调用 Camera.release() 后正在使用相机

理想情况下,当按下后退按钮时应该返回到上一个活动,当按下主页按钮时应该重新打开没有任何问题。

有人可以指导我完成这个调用的良好实现,例如释放和打开相机:

自定义表面视图类:

public class ImageSurfaceView extends SurfaceView implements 
SurfaceHolder.Callback {
private Camera camera;
private SurfaceHolder surfaceHolder;
public final String TAG = ImageSurfaceView.class.getSimpleName();

public ImageSurfaceView(Context context, Camera camera) {
    super(context);
    this.camera = camera;
    this.surfaceHolder = getHolder();
    this.surfaceHolder.addCallback(this);
}


@Override
public void surfaceCreated(SurfaceHolder holder) {
    try {
        this.camera.setPreviewDisplay(holder);
        this.camera.startPreview();
    } catch (IOException ex){
        Log.e(TAG, "surfaceCreated: "+ex.getMessage() );
    }

}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    this.camera.stopPreview();
    this.camera.release();
    this.camera = null;
}

}

还有我的 CameraAcitivty LifeCycle 回调:

@Override
protected void onRestart() {
    super.onRestart();
    camera = null;
    requestCamera();
}

@Override
protected void onResume() {
    super.onResume();
    try {
        requestCamera();
    } catch (RuntimeException ex){
        Log.e(TAG, "onResume: "+ex.getMessage() );
    }
}

@Override
protected void onPause() {
    super.onPause();
    if (camera != null) {
        camera.setPreviewCallback(null);
        imageSurfaceView.getHolder().removeCallback(imageSurfaceView);
        camera.release();
        camera = null;
    }
}

@Override
protected void onStop() {
    super.onStop();
    isSurfaceCreated = false;
}

@Override
protected void onDestroy() {
    super.onDestroy();
    releaseCameraAndPreview();
}

private void releaseCameraAndPreview(){

    if (camera != null) {
        camera.stopPreview();
        camera.release();
        camera = null;
    }
    if(imageSurfaceView != null){
        imageSurfaceView.destroyDrawingCache();
    }
}

private void requestCamera(){
    if (camera == null) {
        if (checkPermission()) {
            callCameraThread();
            Toast.makeText(getApplicationContext(), "Permission already granted", Toast.LENGTH_LONG).show();
        } else {
            requestPermission();
        }
    }
}

public Camera checkDeviceCamera(){
    Camera mCamera = null;
    try {
        mCamera = Camera.open(0);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return mCamera;
}

private void callCameraThread(){
    if(mThread == null){
        mThread = new CameraHandlerThread();
    }

    synchronized (mThread){
        mThread.openCamera();
    }
}

public synchronized void loadSurface(){
    while(camera == null){
        try {
            this.wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    if(!isSurfaceCreated) {
        imageSurfaceView = new ImageSurfaceView(CameraActivity.this, camera);
        cameraPreviewLayout.addView(imageSurfaceView);
        isSurfaceCreated = true;
        imgGhost = new ImageView(this);
        imgGhost.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        imgGhost.setBackground(ContextCompat.getDrawable(this, R.drawable.fantasma_doc_front));
        cameraPreviewLayout.addView(imgGhost);
    }
}

private CameraHandlerThread mThread = null;
private class CameraHandlerThread extends HandlerThread {
    Handler mHandler = null;

    CameraHandlerThread(){
        super("CameraHandlerThread");
        start();
        mHandler = new Handler(getLooper());
    }

    synchronized  void notifyCameraOpened(){
        notifyAll();
    }

    void openCamera(){
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                camera = checkDeviceCamera();
                notifyCameraOpened();
            }
        });
        try{
            wait();
        } catch (InterruptedException ex){
            Log.e(TAG, "openCamera: "+"wait was interrupted" );
        }
    }
}

我不知道如何正确处理相机的释放和表面视图的创建,现在我做了一些更改后,我可以按主页按钮并毫无问题地返回,但是 onBackPressed 崩溃,之前是相反的,谢谢提前在这件事上寻求任何帮助

【问题讨论】:

  • 简单的生命周期是打开相机 onResume 并释放它 onPause 。对此的所有改进都不能改变这个基本流程。我认为您不需要在 openCamera() 中等待(),您的 UI 线程可以在后台 HandlerThread 打开相机时继续。
  • 感谢您的澄清,帮我解决了很多问题
  • 我等待的原因是因为如果相机为 null ,surfaceView 会抛出异常,我不确定是否 100% 正确,但在我的情况下
  • 我得到了它的工作,虽然我不确定这是最好的方法,我明天会发布代码,你的话也很有趣,我明天也会进入,也许我是一直不理解它们,但是您能否举一个简短的例子来说明如何使用 notifyCameraOpened() 以及关于第二个问题 releaseCameraAndPreview() ,在 onPause() 之后肯定会调用 surfaceDestroyed,但我没有使用同步方法,也很高兴看到这种方法。谢谢亚历克斯
  • 嗨@AlexCohn,我发布了我的答案,我希望你能看看并提供一些反馈,我想问一些如何实施你建议的解决方案的例子,因为我的兴趣是最佳实践。

标签: android android-camera android-lifecycle camera-api


【解决方案1】:

所以,我几乎尽量减少对相机的重复引用,这是 onBackPressed 的样子:

@Override
public void onBackPressed() {
    if (isPreviewing) {  //if previewing layout is on screen, then I want to return to the camera, without saving the picture
        rlPicturePreview.setVisibility(View.GONE);
        llCameraControl.setVisibility(View.VISIBLE);
        isPreviewing = false;
        isPictureProcessing = false;
        camera.startPreview();
    } else {
        if(!isPictureProcessing) {   //Otherwise if there is no process happening I want to go back to previous calling activity, therefore closing the camera
            previousDataExists = false;
            isSurfaceCreated = false;
            DriverFragment.docPicturesArray.clear();
            CameraDocActivity.this.finish();
        }
    }
}

这就是 onResume() 现在的样子,我添加了对在棒棒糖、棉花糖和牛轧糖中测试的大多数现代 API 的支持

@Override
protected void onResume() {
    super.onResume();
    try {
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M){
            callCameraThread();
            loadSurface();
        } else {
            requestCamera();
        }
    } catch (RuntimeException ex){
        Log.e(TAG, "onResume: "+ex.getMessage() );
    }
}

在 onRestart 中删除 requestCamera():

@Override
protected void onRestart() {
    super.onRestart();
    camera = null;
}

最后在 onSurfaceDestroyed() 中进行 null 验证:

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    if(this.camera != null) {
        this.camera.stopPreview();
        this.camera.release();
        this.camera = null;
    }

现在按下主页按钮时没有任何问题,或者如果按下后退按钮,我确保相机在每次通过 onRestart()、关闭后和 onStop 时都设置为 null标志 isSurfaceCreated 设置为 false。

【讨论】:

  • 这肯定现在看起来好多了,但我不知道 CameraDocActivity 是什么以及你为什么在 onBackPressed 中杀死它
  • CameraDocActivity 是按下后退按钮时承载相机的活动跨度>
  • 我还有一个按钮可以确认拍照并通过界面通知
  • 如果您的 CameraActivity 处理输入(并接收 onBackPressed()),其他活动将被暂停。另一方面,如果它在后台运行一些图片处理(并保存到磁盘),finish() 不会中断它。 finish() 仅标记要关闭的活动,异步。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-09-04
  • 2017-05-03
  • 2013-11-26
  • 2017-12-27
  • 1970-01-01
  • 1970-01-01
  • 2014-09-10
相关资源
最近更新 更多