0

我正在使用SurfaceView在相機預覽頂部繪製,從this tutorial調整代碼。該應用程序首先工作,然後崩潰的方向變化,有時在第一次更改,有時更改兩次或三次後。我見過很多類似的問題,但沒有一個解決方案(針對我的情況)。這是個例外:SurfaceView在方向更改時崩潰

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" /> 
+0

您應該閱讀並瞭解活動生命週期。除非使用最簡單的Android應用程序,否則不可能構建任何東西,除非使用它。然後,再看看你的問題。我懷疑在onPause()中停止相機可能會做到這一點。 – Simon 2013-05-09 21:46:10

+0

@Simon是的,我知道Activity的生命週期,並且實現onPause/Resume沒有幫助。如果你能指出我的例子中生命週期問題的出現,我會非常感激。 – jaybee 2013-05-09 21:54:58

回答

1

在我看來,你的錯誤從調用自stopCamera()方法camera.release後camera.stopPreview()()出現可能是被調用兩次,一次在onDestroy()並再次在onSurfaceDestroyed()中。如果您在相應的拆卸代碼之後將camera和cameraSH設置爲null,它將防止發生這種情況。建議在stopCamera()中修復:

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

謝謝 - 這似乎是一個非常合理的建議,但我嘗試時卻遇到同樣的錯誤。 – jaybee 2013-05-10 01:30:40

+0

嗯有趣,你可以添加一些日誌語句到你的Activity和SurfaceView回調?這將有助於縮小你已經發布後調用攝像頭方法的範圍。 – 2013-05-10 14:19:27