释放()异常后调用的方法无法使用android相机恢复

Posted

技术标签:

【中文标题】释放()异常后调用的方法无法使用android相机恢复【英文标题】:method called after release() exception unable to resume with android camera 【发布时间】:2012-02-10 17:29:01 【问题描述】:

在开发相机应用时,我遇到了一个仅在我切换到其他应用时发生的异常(我的应用为onPause())。

01-15 17:22:15.017: E/androidRuntime(14336): FATAL EXCEPTION: main
01-15 17:22:15.017: E/AndroidRuntime(14336): java.lang.RuntimeException: Method called after release()
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.hardware.Camera.setPreviewDisplay(Native Method)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.hardware.Camera.setPreviewDisplay(Camera.java:357)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at com.sora.cbir.yuki.image.leaf.CameraPreview.surfaceCreated(CameraPreview.java:32)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.view.SurfaceView.updateWindow(SurfaceView.java:551)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.view.SurfaceView.onWindowVisibilityChanged(SurfaceView.java:213)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.view.View.dispatchWindowVisibilityChanged(View.java:4075)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:742)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:742)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:742)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.view.ViewGroup.dispatchWindowVisibilityChanged(ViewGroup.java:742)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.view.ViewRoot.performTraversals(ViewRoot.java:858)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.view.ViewRoot.handleMessage(ViewRoot.java:1995)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.os.Handler.dispatchMessage(Handler.java:99)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.os.Looper.loop(Looper.java:150)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at android.app.ActivityThread.main(ActivityThread.java:4389)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at java.lang.reflect.Method.invokeNative(Native Method)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at java.lang.reflect.Method.invoke(Method.java:507)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:849)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607)
01-15 17:22:15.017: E/AndroidRuntime(14336):    at dalvik.system.NativeStart.main(Native Method)

我做了一些研究,发现我需要添加

mCamera.setPreviewCallback(null);

作为 Android 相机堆栈的解决方法

我的onPause() 现在看起来像这样:

@Override
protected void onPause() 
    super.onPause();
    try
        
        // release the camera immediately on pause event   
        //releaseCamera();
         mCamera.stopPreview(); 
         mCamera.setPreviewCallback(null);
         mCamera.release();
         mCamera = null;

    
    catch(Exception e)
    
        e.printStackTrace();
    

还有我的onResume()

@Override
protected void onResume()

    super.onResume();
    try
    
        mCamera.setPreviewCallback(null);
        mCamera = getCameraInstance();
        //mCamera.setPreviewCallback(null);
        mPreview = new CameraPreview(Imageupload.this, mCamera);//set preview
        preview.addView(mPreview);
     catch (Exception e)
        Log.d(TAG, "Error starting camera preview: " + e.getMessage());
    
   

最后是我的getCameraInstance() 方法:

public Camera getCameraInstance()
    Camera camera = null;
    try 
        camera = Camera.open(); // attempt to get a Camera instance
    
    catch (Exception e)
        // Camera is not available (in use or does not exist)
    
    Camera.Parameters parameters = camera.getParameters();
    //mPreviewSize = getBestPreviewSize(parameters, wt, ht);
    //mPictureSize = getBestPictureSize(parameters, wt, ht);
    //Shift W & H => if camera rotates 90 deg

    mPreviewSize = getOptimalPreviewSize(parameters, wt, ht); //original => wt,ht
    mPictureSize = getOptimalPictureSize(parameters, wt, ht); //original => wt,ht

    Log.d("CAMERA", "SCREEN RESOLUTION H: "+ht);
    Log.d("CAMERA", "SCREEN RESOLUTION W: "+wt);

    Log.d("CAMERA", "PREVIEW RESOLUTION H: "+mPreviewSize.height);
    Log.d("CAMERA", "PREVIEW RESOLUTION W: "+mPreviewSize.width);

    Log.d("CAMERA", "PICTURE RESOLUTION H: "+mPictureSize.height);
    Log.d("CAMERA", "PICTURE RESOLUTION W: "+mPictureSize.width);
    //set preview size based on device screen
    parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    //set picture size based on device screen
    parameters.setPictureSize(mPictureSize.width, mPictureSize.height);
    //set output camera mode
    parameters.setPictureFormat(PixelFormat.JPEG);
    //set focous mode
    parameters.setFocusMode(FOCUS_MODE_AUTO);
    //set flash mode
    parameters.setFlashMode("auto");
    List<int[]> fps = parameters.getSupportedPreviewFpsRange();
    //System.out.println("FPS size: " +fps.size());
    //System.out.println("MAX FPS:"+(fps.get(fps.size()-1)[1])/1000);
    //log min and max camera supported fps
    Log.d("CAMERA", "CAMERA MAX FPS: "+(fps.get(fps.size()-1)[1])/1000);
    Log.d("CAMERA", "CAMERA MIN FPS: "+(fps.get(fps.size()-1)[0])/1000);
    if(camera_fps)
    
        parameters.setPreviewFpsRange(fps.get(fps.size()-1)[1], fps.get(fps.size()-1)[1]);
    
    //set camera parameters
    camera.setParameters(parameters);

    Toast.makeText(getApplicationContext(), "Your device are capable of previewing @" + fps.get(fps.size()-1)[1]/1000+"fps!",Toast.LENGTH_SHORT).show();
    return camera; // returns null if camera is unavailable

关于如何解决这个问题的任何想法?

【问题讨论】:

【参考方案1】:

我也遇到了同样的问题。 mCamera.setPreviewCallback(null); 没有帮助。在我的活动中,我已将此添加到releaseCamera

mPreview.getHolder().removeCallback(mPreview);

现在可以使用了。

【讨论】:

这对我有用。我把它放在mCamera.setPreviewCallback(null)mCamera.release()之间的onPause 也为我工作。谢谢。 这段代码中的mPreview到底是什么?我收到了同样的错误,但似乎没有任何合适类型的对象可以传递给这个方法。我唯一的回调是Camera.PreviewCallback,而不是SurfaceHolder.CallbackmPreview.getHolder() 是你的SurfaceHolder,对吧?) 类似这样的:private CameraPreview mPreview; onResume:mPreview = new CameraPreview(this, mCamera, preview, null); public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback 抱歉,很久以前的事了,不太记得了。【参考方案2】:

@ookami.kb 解决方案也对我有用,@srunni 也发表了评论。

public void onPause() 
    super.onPause();

    if (mCamera != null) 
        mCamera.setPreviewCallback(null);
        mPreview.getHolder().removeCallback(mPreview);
        mCamera.release();
    

我也删除了 onDestroy 方法。

【讨论】:

你为什么还要从 OnDestroy 中删除 - 完全没有意义!【参考方案3】:

文档清楚地说camera.release() 释放所有相机资源。调用后相机参考不能再使用了。

如果您想再次使用相机,您必须通过open(int) 方法获取它。

camera docs 中都有描述。

【讨论】:

你能再检查一下代码吗?我已经添加了您的建议并根据 android dev 的指南修复了其余部分,但仍然没有结果 还是同样的错误?它清楚地写着Method called after release() 和下一行at android.hardware.Camera.setPreviewDisplay(..)。所以你必须在release()之后调用setPreviewDisplay() 在我的 onResume 中,我需要恢复我的预览,因此,使用 mCamera.open()...,然后设置我的所有参数并再次预览显示表面,对吗?必须在 release() 之后调用。如果这导致异常,我该如何正确恢复它? 无论如何,我已经用 try catch 语句包围了我的 setPreviewDisplay(..),现在系统将报告异常但继续我的任务;我会将此视为一种解决方法:)如果您认为不是这样,非常欢迎您添加任何内容:) 这不是正确的答案。根据其他答案,removeCallback() 解决了该问题。来自谷歌的文档不完整。【参考方案4】:

要正确恢复,您需要这样做:

@Override
public void onResume() 
    super.onResume();  

    // Get the Camera instance as the activity achieves full user focus
    if (mCamera == null) 
        initializeCamera(); // Local method to handle camera initialization
    




protected void initializeCamera()
    // Get an instance of Camera Object
    mCamera = getCameraInstance();

   // create a basic camera preview class that can be included in a View layout.
    mPreview=new CameraPreview(this,mCamera);

    //add your preview class to the FrameLayout element.
    preview.addView(mPreview);

   //Trigger capturing an image by calling the Camera.takePicture() method.
    captureButton.setOnClickListener(
            new View.OnClickListener() 
                @Override
                public void onClick(View v) 
                    // get an image from the camera
                    mCamera.takePicture(null, null, mPicture);
                
            
        );

同时也提醒一下,在 oncreate() 中除了定义 FrameLayout 预览和 Button captureButton 之外什么都不做。

【讨论】:

【参考方案5】:
@Override public void surfaceDestroyed(SurfaceHolder surfaceHolder) 

    this.getHolder().removeCallback(this);
    mCamera.stopPreview();
    mCamera.release();
    mCamera = null;
  Log.e("surfaceDestroyed", "surfaceDestroyed");


并在 Resume 函数中重新初始化相机。

【讨论】:

【参考方案6】:

添加到 okambi 的答案。

这是恢复时搞砸一切的功能:

 public void surfaceCreated(SurfaceHolder holder) 
        // The Surface has been created, now tell the camera where to draw the preview.
        try 
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
         catch (IOException e) 
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        
    

try 没有捕捉到抛出的异常。即mCamera不存在,然后当它尝试调用setPreviewDisplay(holder)时,出现crash。

因此,通过删除回调,不会调用此 surfaceCreated 并避免崩溃。

Google 对此的记录很差。

【讨论】:

【参考方案7】:

我放了

mPreview.getHolder().removeCallback(mPreview);  

之间。

mCamera.setPreviewCallback(null);

mCamera.release();

它对我有用。

 @Override
            protected void onPause() 
                super.onPause();

                this.saveTextEdits();
                try 
                    mCamera.stopPreview();
                    mCamera.setPreviewCallback(null);
                    **mPreview.getHolder().removeCallback(mPreview);**
                    mCamera.release();
                    mCamera = null;
                catch (Exception e)

                
            

【讨论】:

【参考方案8】:

我遇到了同样的问题,我通过 - 在 Preview 类的 surfaceDestroyed(SurfaceHolder holder) 方法中添加 mCamera = null;

public void surfaceDestroyed(SurfaceHolder holder) 
    // Surface will be destroyed when we return, so stop the preview.
    if (mCamera != null) 
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    

和 - 添加

camera = Camera.open();
    camera.startPreview();
    params = camera.getParameters();
    preview.setCamera(camera);

在我的 CameraActivity 的 OnResume() 方法中。

【讨论】:

【参考方案9】:

如果你得到:

尝试调用虚方法'void android.hardware.Camera.setPreviewCallback(android.hardware.Camera$PreviewCallback)' 在空对象引用上

我同意@ookami.kb - mCamera.setPreviewCallback(null); 还不够,后面还要加上这个:

mCameraView.getHolder().removeCallback(mCameraView);

【讨论】:

以上是关于释放()异常后调用的方法无法使用android相机恢复的主要内容,如果未能解决你的问题,请参考以下文章

如何修复 Android 模拟器中的“无法连接到相机服务”异常

使用多个活动时何时正确释放相机?

如何在构造函数中发生异常时释放API使用的资源?

android虚拟机的相机为啥无法使用

相机打开时如何释放相机?

RuntimeException:无法连接到相机服务?