释放后调用的方法 引发异常 相机预览

11

我有一个活动类(CameraActivity),它使用我的CameraPreview类。在"OnResume"中初始化相机和预览。在"OnPause"中,我释放相机资源。当应用程序启动时,在"OnResume"内部一切正常,但是当我通过意图启动另一个活动(例如,在浏览器中打开网址)然后返回到我的活动时,异常发生在CamerPreview类中的"OnResume"内。请查看以下代码:

// CameraActivity类

public void onResume(){
    super.onResume();
    Log.d("inside onResume, camera==="+mCamera, "inside onResume");
    try {
        if(mCamera==null)
        {

    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);


        autoFocusHandler = new Handler();
        mCamera = getCameraInstance();
        int rotation = this.getWindowManager().getDefaultDisplay().getRotation();


        scanner = new ImageScanner();
        scanner.setConfig(0, Config.X_DENSITY, 3);
        scanner.setConfig(0, Config.Y_DENSITY, 3);

        mPreview = new CameraPreview(this, mCamera, previewCb, autoFocusCB);

        FrameLayout preview = (FrameLayout)findViewById(R.id.cameraPreview);
        preview.addView(mPreview);



    }





} catch (Exception e) {
    // TODO Auto-generated catch block
    Log.e("onResume",Log.getStackTraceString(e));
}



public void onPause{
    try {
        super.onPause();
        if (mCamera != null) {
            previewing = false;
            mCamera.stopPreview();
                mCamera.setPreviewCallback(null);
                mCamera.release();
                mCamera = null;
                mPreview=null;


    }
} catch (Exception e) {
    // TODO Auto-generated catch block
    Log.e("releaseCamera",Log.getStackTraceString(e));
}
}



 public static Camera getCameraInstance(){
        Camera c = null;
        try {
            c = Camera.open();
        } catch (Exception e){
            Log.e("getCameraInstance",Log.getStackTraceString(e));

    }
    return c;
}

// 以下是CameraPreview类:

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder mHolder;
    Camera mCamera;
    PreviewCallback previewCallback;
    AutoFocusCallback autoFocusCallback;
    private int rotation;
    public int getRotation() {
        return rotation;
    }

    public void setRotation(int rotation) {
        this.rotation = rotation;
    }

    public CameraPreview(Context context, Camera camera,
                         PreviewCallback previewCb,
                         AutoFocusCallback autoFocusCb) {
        super(context);
        mCamera = camera;
        previewCallback = previewCb;
        autoFocusCallback = autoFocusCb;

        /*
         * Set camera to continuous focus if supported, otherwise use
         * software auto-focus. Only works for API level >=9.
         */
        /*
        Camera.Parameters parameters = camera.getParameters();
        for (String f : parameters.getSupportedFocusModes()) {
            if (f == Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) {
                mCamera.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
                autoFocusCallback = null;
                break;
            }
        }
        */

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = getHolder();
        mHolder.addCallback(this);

        // deprecated setting, but required on Android versions prior to 3.0
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }


    public void surfaceCreated(SurfaceHolder holder) {
        // The Surface has been created, now tell the camera where to draw the preview.
        try {
            if(mCamera==null){
                mCamera=Camera.open();
            }
            mCamera.setPreviewDisplay(holder);
        } catch (IOException e) {
            Log.d("DBG", "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {

        // Camera preview released in activity
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        /*
         * If your preview can change or rotate, take care of those events here.
         * Make sure to stop the preview before resizing or reformatting it.
         */
        if (mHolder.getSurface() == null){
          // preview surface does not exist
          return;
        }

        // stop preview before making changes
        try {
            mCamera.stopPreview();
        } catch (Exception e){
          // ignore: tried to stop a non-existent preview
        }

try{
            mCamera.setPreviewDisplay(mHolder);
            mCamera.setPreviewCallback(previewCallback);
            mCamera.startPreview();
            mCamera.autoFocus(autoFocusCallback);
        } catch (Exception e){
            Log.d("DBG", "Error starting camera preview: " + e.getMessage());
        }
    }

}

这是来自logCat的信息:

11-05 10:14:34.585: E/AndroidRuntime(7864): FATAL EXCEPTION: main
11-05 10:14:34.585: E/AndroidRuntime(7864): java.lang.RuntimeException: Method called after release()
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.hardware.Camera.setPreviewDisplay(Native Method)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.hardware.Camera.setPreviewDisplay(Camera.java:393)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at com.intagleo.qraugmented.detection.camera.CameraPreview.surfaceCreated(CameraPreview.java:74)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.view.SurfaceView.updateWindow(SurfaceView.java:552)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:215)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.view.View.dispatchWindowVisibilityChanged(View.java:4027)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:720)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:720)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:720)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:720)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:720)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.view.ViewRoot.performTraversals(ViewRoot.java:790)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1867)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.os.Handler.dispatchMessage(Handler.java:99)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.os.Looper.loop(Looper.java:130)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at android.app.ActivityThread.main(ActivityThread.java:3687)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at java.lang.reflect.Method.invokeNative(Native Method)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at java.lang.reflect.Method.invoke(Method.java:507)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
11-05 10:14:34.585: E/AndroidRuntime(7864):     at dalvik.system.NativeStart.main(Native Method)

编辑

我按建议更新了“surfaceDestroyed”并添加了日志,但现在我在“onPause”-> onSurfaceDestroyed中遇到异常。最初,onPause执行得很好。

1- 摄像机实例是通过活动类的“getCameraInstance”方法在“onResume”中创建的,并将mCamera实例传递给CameraPreview类。我尝试更改它,使摄像机实例仅在“onSurfaceCreated”中创建,并将mCamera实例重新分配给活动类,但它没有起作用。我还通过调试注意到,“CameraPreview”类的“previewCallBack”成员第一次有效,但第二次“previewCallBack”成员为空。

请注意,第一次调用“onResume”时,一切都正常工作,但当它在onPause之后第二次运行时,即使代码在onResume中相同,也会发生异常。

11-06 01:25:28.375: I/onResume(4332): INITIATED
// Workinf fine till now. Now opening another intent activity
11-06 01:26:23.500: I/onPause(4332): INITIATED
11-06 01:26:23.804: "OnSurfaceDestroyed": "Initiated"
11-06 01:26:23.945: E/AndroidRuntime(4332): FATAL EXCEPTION: main
11-06 01:26:23.945: E/AndroidRuntime(4332): java.lang.RuntimeException: Method called after release()
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.hardware.Camera.stopPreview(Native Method)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at com.intagleo.qraugmented.detection.camera.CameraPreview.surfaceDestroyed(CameraPreview.java:85)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.view.SurfaceView.reportSurfaceDestroyed(SurfaceView.java:596)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.view.SurfaceView.updateWindow(SurfaceView.java:490)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:215)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.view.View.dispatchWindowVisibilityChanged(View.java:4027)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:720)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:720)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:720)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:720)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:720)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.view.ViewRoot.performTraversals(ViewRoot.java:790)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1867)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.os.Handler.dispatchMessage(Handler.java:99)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.os.Looper.loop(Looper.java:130)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at android.app.ActivityThread.main(ActivityThread.java:3687)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at java.lang.reflect.Method.invokeNative(Native Method)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at java.lang.reflect.Method.invoke(Method.java:507)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
11-06 01:26:23.945: E/AndroidRuntime(4332):     at dalvik.system.NativeStart.main(Native Method)

在我看来,你的相机被正确释放了,但是当恢复时它无法打开。在 surfaceDestroyed 中将 mCamera 设置为 null,这样它就会通过 if(mCamera==null) { 语句在 surfaceCreated 中执行。 - jnthnjns
请将下面的内容标记为已解决我的问题。 - droid kid
@arunsoorya 哪一个答案? - Muhammad Imran Tariq
@ImranTariq 回答得票最高的。 - droid kid
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Rahul Sharma
7个回答

42

您已将相机添加到FrameLayout中,如下所示:

FrameLayout preview = (FrameLayout)findViewById(R.id.cameraPreview);
preview.addView(mPreview);

将会调用surfaceCreated方法,因此会调用mCamera.setPreviewDisplay(holder);

当您创建/打开一个新相机时,FrameLayout 仍然具有以前的相机,因此它的surfaceCreated方法将与您的新相机一起被调用。

您应该在释放相机时(在 onPause() 方法中)从FrameLayout中删除以前的相机,像这样:

preview.removeView(mPreview);

希望这可以帮到你。


5
丹是正确的。还可以参见此处
代码示例:
public class MainActivity extends Activity {
    private FrameLayout mFlCameraPreview;
    private Camera mCamera;
    private CameraPreview mCameraPreview;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mFlCameraPreview = (FrameLayout) findViewById(R.id.main_fl_camera_preview);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mCamera == null) {
            mCamera = getCameraInstance();
        }

        if (mCameraPreview == null) {
            mCameraPreview = new CameraPreview(this, mCamera);
            mFlCameraPreview.addView(mCameraPreview);
        }

    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }

        if (mCameraPreview != null) {
            mFlCameraPreview.removeView(mCameraPreview);
            mCameraPreview = null;
        }
    }

    public static Camera getCameraInstance() {
        Camera camera = null;
        try {
            camera = Camera.open();
        } catch (Exception e) {

        }
        return camera;
    }
}  

1

编辑

这是一个难以回答的问题,我现在无法百分之百确定是什么原因导致了您的异常。我在下面包含了我的相机代码,希望能有所帮助。我使用startCamera()stopCamera()方法,在onPauseonResume中调用。此外,我只在onCreate中创建CameraPreview类的新实例,除非cameraView == null,否则不会在onResume中重新实例化。我们有一些不同的做法。希望下面的代码能够帮助您,也许您可以尝试修改它来使您的代码正常运行:

P.S.:对我来说一切都正常,即进入其他活动等。我没有测试的唯一生命周期部分是onDestroy,但这是因为我的应用程序被设计为在此周期的开头开始。

MainActivity:

boolean cameraReleased = false;

@Override
protected void onPause() {
    Log.i("onPause", "CALLED:: cameraReleased = " + cameraReleased);
    Log.i("onResume", "CALLED:: cameraView = " + cameraView.toString());
    if (cameraReleased == false) {
        image = null;
        imageResult.setImageBitmap(null);
        imageResult.setImageResource(0);
        cameraView.stopCamera();
        cameraReleased = true;
    }

    if (cameraView == null) {
        Log.i("onPause", "cameraView == null");
        cameraView = new JJCameraSurfaceView(getApplicationContext());
        imageResult = new ImageView(getApplicationContext());
    }
    super.onPause();
}

@Override
protected void onDestroy() {
    Log.e("onDestroy", "INITIATED");
    super.onDestroy();
}

@Override
protected void onResume() {
    Log.i("onResume", "CALLED:: cameraReleased = " + cameraReleased);
    Log.i("onResume", "CALLED:: cameraView = " + cameraView.toString());
    if (cameraReleased == true) {
        image = null;
        imageResult.setImageBitmap(null);
        imageResult.setImageResource(0);
        cameraView.startCamera();
    }

    if (cameraView == null) {
        Log.i("onResume", "cameraView == null");
        cameraView = new JJCameraPreview(getApplicationContext());
        imageResult = new ImageView(getApplicationContext());
    }

    super.onResume();
}

@Override
public void onBackPressed() {
    // If Statement used to get out of my camera view and back to my MainActivity - Same Class
    if (“Camera Preview or Image Result is displayed”) {
        cameraView.stopCamera();
        image = null;
        imageResult.setImageBitmap(null);
        imageResult.setImageResource(0);
        cameraView.startCamera();
        return;
    }

    Log.i("onBackPressed", "WAS PRESSED");
    super.onBackPressed();
}

相机预览:

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    Log.w("surfaceChanged", "STARTED");
    if (camera != null) {
        Log.w("surfaceChanged", "camera = NOT NULL");
        Camera.Parameters cParams = camera.getParameters();
        cParams.setPreviewSize(width, height);
        cParams.setSceneMode(Parameters.SCENE_MODE_NIGHT);
        camera.setParameters(cParams);
        camera.startPreview();
    }
}

public void surfaceCreated(SurfaceHolder holder) {
    Log.w("surfaceCreated", "STARTED");
    if (camera == null) {
        camera = Camera.open(); 
    }
    try {
        camera.setPreviewDisplay(mHolder);
    } catch (Exception e) {
        Log.e("setPreviewDisplay", "FAILED: " + e.getMessage());
    }
}

public void surfaceDestroyed(SurfaceHolder holder) {
    Log.w("CameraSurfaceDestroyed", "INITIATED");
    camera.stopPreview();
    camera.release();
    camera = null;
}

public void startCamera() {
    Log.w("startCamera", "CALLED");
    mHolder = getHolder();
    surfaceCreated(mHolder);
    camera.startPreview();
    mHolder.addCallback(this);
}

public void stopCamera() {
    mHolder = getHolder();
    mHolder.removeCallback(this);
    camera.stopPreview();
}

mCamera应该为null,因为CameraPreview是一个新创建的实例(在onPaused中被置空)。 - njzk2
你的相机没有被正确地重新打开。我最近也遇到了这个问题,不得不覆盖所有Activity生命周期方法,以找出哪个方法被调用,并确保我正确释放和重新打开相机。如果onPauseonResume与您的相机正常工作,那么当您通过意图启动其他活动时,可能没有调用onPause - jnthnjns
如果没有调用onPause,那么release也不会被调用,对吧? - njzk2
是的,你说得对,除非你在代码中的其他地方调用了释放相机的方法并且没有将“mCamera”设置为“null”。 - jnthnjns
你能在你的onPause方法中添加Log.i("onPause", "INITIATED");,并且在surfaceCreated方法中的if语句之前添加Log.i("surfaceCreated", "" + mCamera);吗?这样我们可以更好地看到正在调用什么。你得到的异常非常令人困惑。 - jnthnjns
@imrantariq 我已经在我的编辑答案中包含了我的代码。希望这可以帮助你。这是100%工作的代码,但请注意:我的应用程序是为我的雇主开发的,他们目前只让我在API 15上开发。 - jnthnjns

1

我遇到了类似的问题,我的代码出现了 "Method called after release()" 错误导致强制关闭。

我在 OnResume() 中调用了一个 SetupCamera() 方法,该方法检查了一个空的 camera 对象,然后调用了 camera.Open()

解决我的问题的方法是摆脱了对空值的检查,无论它是否为空,都调用了 camera.Open()(我已经在 onPause 中设置了 camera = null),这是在阅读 Camera Docs 后得出的结论。

我知道这并不能彻底追踪我的问题,但它绝对对我有效!


1
我的工作解决方案: 首先: 在CameraActivity类中声明mCamera为数组:
Camera mCamera[] = new Camera[1];

第二步:构造函数CameraPreview的声明必须如下所示:
public CameraPreview(Context context, Camera[] camera,
                     PreviewCallback previewCb,
                     AutoFocusCallback autoFocusCb) {
      mCamera = camera;
      ...
}

但是CameraPreview类中的mCamera必须声明如下:

Camera[] mCamera; // without instantiating of the array!

最后:在两个类的所有方法中,您应该将所有引用mCamera替换为mCamera[0] 附言:对于我的英语表示抱歉。

0
尝试使用下面的代码,而不是覆盖onPause()和onResume()方法,覆盖onStop()和onRestart()方法。在活动生命周期中,当活动不可见时,将调用onStop()方法,并且下一个生命周期方法调用将是onRestart()方法。请查看下面的代码。
@Override
    protected void onStop() {
        super.onStop();
        try {
            m_camera.stopPreview();
            m_camera.release();
            preview.removeView(m_CameraPreview);
/*
m_CameraPreview is the object of the class that looks like this :                                public class CameraSurfaceView extends SurfaceView implements Callback
*/
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
    }
    @Override
    protected void onRestart() {
        super.onRestart();
        m_camera=getCameraInstance();//Initialize the camera in your own way
        m_CameraPreview = new CameraSurfaceView(this, m_camera);
       preview = (FrameLayout)findViewById(R.id.camera_preview);   
        preview.addView(this.m_CameraPreview);
/* 
*camera_preview is the id of the framelayout defined in xml file and preview is *the instance of FrameLayout. 
*/
    }

正如Dan所说,框架布局将保留先前相机实例的引用,并且它的surfaceview回调将与新对象一起创建,从而创建了竞争条件。因此,您需要在onStop()中释放它,并在onRestart()中重新初始化。 希望这可以帮助到您。

0

我也遇到了同样的错误。以下步骤解决了我的问题。

surfaceDestroyed() 中调用 getHolder().removeCallback(this);

调用

 private void releaseCameraAndPreview() {

        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.setPreviewCallback(null);
            mCamera.release();
            mCamera = null;
        }

        if(mPreview != null){
            mPreview.destroyDrawingCache();
            mPreview.mCamera = null;
        }

    }

onPause

调用

  private boolean safeCameraOpenInView(View view) {
        boolean qOpened = false;
        releaseCameraAndPreview();
        mCamera = getCameraInstance();
        mCameraView = view;
        qOpened = (mCamera != null);

        if(qOpened == true){
            mPreview = new CameraPreview(getActivity().getBaseContext(), mCamera,view);
            preview = (FrameLayout) view.findViewById(R.id.camera_preview);
            preview.addView(mPreview);
            mPreview.startCameraPreview();
        }
        return qOpened;
    }

onCreateView()中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接