SurfaceView在方向更改时崩溃

时间:2013-05-09 21:39:01

标签: android android-camera android-canvas android-orientation

我使用SurfaceView在相机预览之上绘制,调整this tutorial的代码。应用程序首先运行然后在方向更改时崩溃,有时在第一次更改时,有时在更改两次或三次后崩溃。我见过很多类似的问题,但没有一个解决方案(对我而言)。这是例外:

05-09 22:14:48.384: D/libEGL(829): loaded /vendor/lib/egl/libEGL_POWERVR_SGX540_120.so
05-09 22:14:48.400: D/libEGL(829): loaded /vendor/lib/egl/libGLESv1_CM_POWERVR_SGX540_120.so
05-09 22:14:48.408: D/libEGL(829): loaded /vendor/lib/egl/libGLESv2_POWERVR_SGX540_120.so
05-09 22:14:48.486: D/OpenGLRenderer(829): Enabling debug mode 0
05-09 22:14:49.056: I/Choreographer(829): Skipped 40 frames!  The application may be doing too much work on its main thread.
05-09 22:14:49.337: D/dalvikvm(829): GC_FOR_ALLOC freed 113K, 2% free 8736K/8876K, paused 50ms, total 64ms
05-09 22:14:49.353: I/dalvikvm-heap(829): Grow heap (frag case) to 11.521MB for 3110416-byte allocation
[snip lots]
05-09 22:14:56.423: D/AndroidRuntime(829): Shutting down VM
05-09 22:14:56.423: W/dalvikvm(829): threadid=1: thread exiting with uncaught exception (group=0x4180a930)
05-09 22:14:56.439: E/AndroidRuntime(829): FATAL EXCEPTION: main
05-09 22:14:56.439: E/AndroidRuntime(829): java.lang.RuntimeException: Method called after release()
05-09 22:14:56.439: E/AndroidRuntime(829):  at android.hardware.Camera.setHasPreviewCallback(Native Method)
05-09 22:14:56.439: E/AndroidRuntime(829):  at android.hardware.Camera.access$600(Camera.java:131)
05-09 22:14:56.439: E/AndroidRuntime(829):  at android.hardware.Camera$EventHandler.handleMessage(Camera.java:784)
05-09 22:14:56.439: E/AndroidRuntime(829):  at android.os.Handler.dispatchMessage(Handler.java:99)
05-09 22:14:56.439: E/AndroidRuntime(829):  at android.os.Looper.loop(Looper.java:137)
05-09 22:14:56.439: E/AndroidRuntime(829):  at android.app.ActivityThread.main(ActivityThread.java:5041)
05-09 22:14:56.439: E/AndroidRuntime(829):  at java.lang.reflect.Method.invokeNative(Native Method)
05-09 22:14:56.439: E/AndroidRuntime(829):  at java.lang.reflect.Method.invoke(Method.java:511)
05-09 22:14:56.439: E/AndroidRuntime(829):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
05-09 22:14:56.439: E/AndroidRuntime(829):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
05-09 22:14:56.439: E/AndroidRuntime(829):  at dalvik.system.NativeStart.main(Native Method)
05-09 22:14:58.236: I/Process(829): Sending signal. PID: 829 SIG: 9

我的主要活动包含相机预览:

public class MainActivity extends Activity implements SurfaceHolder.Callback, LocationListener {

private Camera camera;
private SurfaceView cameraSV;
private SurfaceHolder cameraSH;
private OverlayView overlay;

/* Activity event handlers */
// Called when activity is initialised by OS
@Override
public void onCreate(Bundle inst) {
    super.onCreate(inst);
    setContentView(R.layout.activity_main);
    initCamera();
}

// Called when activity is closed by OS
@Override
public void onDestroy() {
    super.onDestroy();
    // Turn off the camera
    stopCamera();
}

/* SurfaceHolder event handlers */
// Called when the surface is first created
public void surfaceCreated(SurfaceHolder holder) {
}

// Called when surface dimensions etc change
public void surfaceChanged(SurfaceHolder sh, int format, int width,
        int height) {
    // Start camera preview
    startCamera(sh, width, height);
}

// Called when the surface is closed/destroyed
public void surfaceDestroyed(SurfaceHolder sh) {
    stopCamera();
}

private void initCamera() {
    cameraSV = (SurfaceView) findViewById(R.id.surface_camera);
    cameraSH = cameraSV.getHolder();
    cameraSH.addCallback(this);

    camera = Camera.open();

    overlay = (OverlayView) findViewById(R.id.surface_overlay);
    overlay.getHolder().setFormat(PixelFormat.TRANSLUCENT);
    overlay.setCamera(camera);
}

// Setup camera based on surface parameters
private void startCamera(SurfaceHolder sh, int width, int height) {
    Camera.Parameters p = camera.getParameters();
    for (Camera.Size s : p.getSupportedPreviewSizes()) { 
        p.setPreviewSize(s.width, s.height);
        overlay.setPreviewSize(s);
        break;
    }
    if (this.getResources().getConfiguration().orientation != Configuration.ORIENTATION_LANDSCAPE) {
        // p.set("orientation", "portrait");
        // p.setRotation(90);
        camera.setDisplayOrientation(90);
    } else {
        // p.set("orientation", "landscape");
        // p.setRotation(0);
        camera.setDisplayOrientation(0);
    }
    camera.setParameters(p);

    try {
        camera.setPreviewDisplay(sh);
    } catch (Exception e) { // Log surface setting exceptions

    }
    camera.startPreview();
}

// Stop camera when application ends
private void stopCamera() {
    if (cameraSH != null) cameraSH.removeCallback(this);
    if (camera != null) {
        camera.stopPreview();
        camera.release();
    }
}   
}

我的自定义SurfaceView类是在相机预览的基础上绘制的:

public class OverlayView extends SurfaceView {
private SurfaceHolder surfaceHolder;
private Camera camera;
private Camera.Size frameSize;

public OverlayView(Context ctx) {
    super(ctx);
    surfaceHolder= getHolder();
}

public OverlayView(Context ctx, AttributeSet attr) {
    super(ctx, attr);
    surfaceHolder = getHolder();
}

public void setPreviewSize(Camera.Size s) {
    frameSize = s;
}

// Called by initCamera, to set callback
public void setCamera(Camera c) {
    camera = c;
    camera.setPreviewCallback(new PreviewCallback() {
        // Called by camera hardware, with preview frame
        public void onPreviewFrame(byte[] frame, Camera c) {
            Canvas canvas = surfaceHolder.lockCanvas(null);
            canvas.drawColor( 0, PorterDuff.Mode.CLEAR );
            try {
                // Perform overlay rendering here
                Paint pt = new Paint();
                pt.setColor(Color.BLACK);
                pt.setTextSize(70);

                canvas.drawText("Hi", 10, frameSize.height-10, pt);
            } catch (Exception e) {
                // Log/trap rendering errors
            } finally {
                surfaceHolder.unlockCanvasAndPost(canvas);
            }
        }
    });
}
}

布局:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">

    <com.example.OverlayView android:id="@+id/surface_overlay"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" />

    <SurfaceView android:id="@+id/surface_camera"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" />
</FrameLayout>

最后,我的清单具有以下权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

1 个答案:

答案 0 :(得分:2)

在我看来,你的错误来自于在camera.release()之后调用camera.stopPreview(),因为stopCamera()方法可能被调用两次,一次在onDestroy()中,再一次在onSurfaceDestroyed()中。如果您在各自的拆卸代码后将camera和cameraSH设置为null,则可以防止这种情况发生。 stopCamera()中的建议修复:

if (cameraSH != null) {
    cameraSH.removeCallback(this);
    cameraSH = null; 
}
if (camera != null) {
    camera.stopPreview();
    camera.release();
    camera = null;
}