2012-03-12 167 views
9

我已经构建了一个应用程序,可以在您触摸预览时拍摄照片。 我可以拍很多照片,但有时当我触摸预览拍照时,没有快门声,整个应用程序冻结。此外,在此之后,如果我尝试启动内置相机应用程序,我会收到相机无法使用的消息。Android相机 - 有时当我拍照时,应用程序冻结,相机不可用

我不知道这种行为的原因,它随机发生,当它发生时,我必须重新启动设备(三星Galaxy S)才能够再次使用相机。

在DDM,碰撞后,可看到下面的行:keyDispatchingTimedOut

下面是相关的代码: CameraActivity类别:

public class CameraActivity extends Activity { 
    private static final String TAG = "CameraDemo"; 
    Preview preview; 

    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 

    preview = new Preview(this); 
    ((FrameLayout) findViewById(R.id.preview)).addView(preview); 
    ((FrameLayout) findViewById(R.id.preview)).setOnTouchListener(preview); 

    Log.d(TAG, "Camera Activity Created."); 

    } 
} 

预览类别:

class Preview extends SurfaceView implements SurfaceHolder.Callback, OnTouchListener { 
    private static final String TAG = "Preview"; 

    SurfaceHolder mHolder; 
    public Camera camera; 
    Context ctx; 
    boolean previewing = false; 

    Preview(Context context) { 
     super(context); 
     ctx = context; 
     // Install a SurfaceHolder.Callback so we get notified when the 
     // underlying surface is created and destroyed. 
     mHolder = getHolder(); 
     mHolder.addCallback(this); 
     mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 
    } 


    // Called once the holder is ready 
    public void surfaceCreated(SurfaceHolder holder) { 
     // The Surface has been created, acquire the camera and tell it where 
     // to draw. 
     camera = Camera.open(); 
    } 

    // Called when the holder is destroyed 
    public void surfaceDestroyed(SurfaceHolder holder) { 

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

     previewing = false; 
    } 

    // Called when holder has changed 
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { 

     if(previewing){ 
      camera.stopPreview(); 
      previewing = false; 
     } 

     if (camera != null){ 
      try { 

       camera.setDisplayOrientation(90); 
       camera.setPreviewDisplay(holder); 
       camera.setPreviewCallback(new PreviewCallback() { 
        // Called for each frame previewed 
        public void onPreviewFrame(byte[] data, Camera camera) { 
         Log.d(TAG, "onPreviewFrame called at: " + System.currentTimeMillis()); 
         Preview.this.invalidate(); 
        } 
       }); 
       camera.startPreview(); 
       previewing = true; 
      } catch (IOException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
     } 
    } 

    public boolean onTouch(View v, MotionEvent event) { 
     camera.takePicture(shutterCallback, rawCallback, jpegCallback); 
     return false; 
    } 


    // Called when shutter is opened 
    ShutterCallback shutterCallback = new ShutterCallback() { 
     public void onShutter() { 
      Log.d(TAG, "onShutter'd"); 
     } 
    }; 

    // Handles data for raw picture 
    PictureCallback rawCallback = new PictureCallback() { 
     public void onPictureTaken(byte[] data, Camera camera) { 
      Log.d(TAG, "onPictureTaken - raw"); 
     } 
    }; 

    // Handles data for jpeg picture 
    PictureCallback jpegCallback = new PictureCallback() { 

     public void onPictureTaken(byte[] data, Camera camera) { 
      FileOutputStream outStream = null; 
      try { 
       // Write to SD Card 
       outStream = new FileOutputStream(String.format("/sdcard/TVguide/Detection/detected.jpg", System.currentTimeMillis())); // <9> 
       outStream.write(data); 
       outStream.close(); 
       Log.d(TAG, "onPictureTaken - wrote bytes: " + data.length); 
      } catch (FileNotFoundException e) { // <10> 
       //Toast.makeText(ctx, "Exception #2", Toast.LENGTH_LONG).show(); 
       e.printStackTrace(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } finally {} 
      Log.d(TAG, "onPictureTaken - jpeg"); 
      Toast.makeText(ctx, "SAVED", Toast.LENGTH_SHORT).show(); 

      camera.startPreview(); 
     } 
    }; 

} 

请帮忙,我想了解几天没有成功的问题

的Eyal

+0

有人吗?我真的坚持 – Eyal 2012-03-14 20:52:10

+0

也我建议将图片保存到SD卡后台线程,而不是主要的UI线程。 – Paul 2013-05-14 17:08:09

回答

6

我不知道是什么原因导致的错误,它会真的帮助,如果你发布的从时间输出的loggcat从这个时候发生错误。

但是,我可以做一些gusesses。它看起来像相机被锁定(内置相机不工作)。如果您的应用程序部队关闭,相机锁可能是由三星相机HAL中错误的错误处理引起的。尤其是在像Galaxy S这样的旧手机中,他们在处理错误的或不标准的API调用方面做得不是最好。

这里有可能是什么造成这种行为的一些建议:

  1. 您应该添加一个后卫拍照。现在,如果您触摸屏幕并拍摄照片,则可以在照片完成拍摄之前再次触摸屏幕。所以,camera.takePicture()将被调用两次。第二个将失败。这是我最好的猜测。

    添加一些boolean isTakingPicture = false变量,然后:

    public boolean onTouch(View v, MotionEvent event) { 
        if (!isTakingPicture) { 
        camera.takePicture(shutterCallback, rawCallback, jpegCallback); 
        isTakingPicture = true; 
        } 
        return false; 
    } 
    ... 
    public void onPictureTaken(byte[] data, Camera camera) { 
        isTakingPicture = false; 
        ... 
    
  2. 你用什么previewCallback呢?我在这里没有做任何有用的事情。预览回调有时可能会导致一些痛苦,尽管您的代码对我来说看起来很好。你可以尝试删除它,并检查是否有帮助。

+0

太棒了!第一个解决方案没有帮助,但第二个解决方案(删除previewCallback)解决了问题 – Eyal 2012-03-15 11:02:24

+0

嗯,也许调用分配给相机的SurfaceView上的invalidate()是问题。我从来没有尝试过,我无法想象任何只读。不过,我认为你也应该应用第一种解决方案,以防止潜在的错误。 – 2012-03-15 11:11:38

8

我刚在三星Galaxy SII上测试我的应用程序时遇到了这个问题。在拍摄照片之前,您只需删除预览回调:

mCamera.setPreviewCallback(null); 
mCamera.takePicture(null, null, mPictureCallback); 
+1

在HTC Sensation上,我的自定义相机应用程序在全屏幕空间中显示预览。当用户点击屏幕时,焦点被调用。当焦点完成并成功时,我称之为mCamera.takePicture。有时它可以工作,并且我可以保存JPG数据的回调函数,其他时候它只停留在takePicture中,没有超时和任何调试消息。这太烦人了,每次都必须重新启动手机,而不知道问题出在哪里。 SettingsPreviewCallback为null不起作用。任何其他想法? – radhoo 2013-07-17 14:48:25

2

我遇到了类似的问题。在LG p705和三星Galaxy Trend上,拍照后,预览被冻结,相机不再可用,直到手机重新启动。然而,在Galaxy S3上,即使在多张照片捕捉之后,预览仍能正常显示。

在调试过程中,我注意到相关监听器类在按下相机按钮拍摄照片时正在接收多个呼叫。我不确定为什么它被调用两次,即使按钮只是单击一次。在任何情况下,由于Tomasz建议使用布尔变量,第二次呼叫将在第一次尝试进行时跳过拍照。也感谢Eyal的提问。 :)

相关问题