Android OnPictureTaken()定时/保存问题

Android OnPictureTaken()定时/保存问题,android,camera,Android,Camera,我正在创建一个自定义相机应用程序(将包含在一个更大的主应用程序中),该应用程序加载带有相机的视图-预览和图片库预览-以便用户可以拍摄一张照片或选择一张先前存在的照片。如果用户确实拍了一张照片,我只想显示一个确认对话框,如果确认了,将控制权传递回主活动。我无法存储照片:需要快速将其用作附件,一旦通过主活动发送,请将其处理掉 当我拍摄一张照片并调用onPictureTaken()时,问题就出现了:它要么不工作,要么保存一张照片需要花费相当长的时间。此外,我似乎不知道如何在不将照片保存到目录的情况下当

我正在创建一个自定义相机应用程序(将包含在一个更大的主应用程序中),该应用程序加载带有相机的视图-预览和图片库预览-以便用户可以拍摄一张照片或选择一张先前存在的照片。如果用户确实拍了一张照片,我只想显示一个确认对话框,如果确认了,将控制权传递回主活动。我无法存储照片:需要快速将其用作附件,一旦通过主活动发送,请将其处理掉

当我拍摄一张照片并调用onPictureTaken()时,问题就出现了:它要么不工作,要么保存一张照片需要花费相当长的时间。此外,我似乎不知道如何在不将照片保存到目录的情况下当场提取照片;我计划立即删除目录,但必须有一种更干净的方法(本质上,我只想访问原始字节数组,而不必保存它)

我希望它是模块化的(在我接下来的项目中会大量使用相机),我认为这是增加整体滞后的原因

我遵循了谷歌的开发者Android,我的大部分代码都是从这里的示例中派生出来的

我也在用ActionBarSherlock

我目前有三门课:

CameraManager

public class CameraManager {

private static Camera CAMERA;

    /** Check if this device has a camera */
    public static boolean  doesDeviceHaveCamera(Context context) {
        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
            return true;
        } 
        else {
            return false;
        }
    }

    public static Camera getCameraInstance(){
        CAMERA = null;
        try {
            CAMERA = Camera.open();
        }
        catch (Exception e){
            // Camera is not available (in use or does not exist)
        }
        return CAMERA;
    }
}
CameraPreview

    public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {

    private static final String TAG = "CameraPreview";
    private SurfaceHolder surfaceHolder;
    private Camera camera;

    public CameraPreview(Context context, Camera camera) {
        super(context);
        this.camera = camera;
        surfaceHolder = getHolder();
        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // deprecated setting, but required on Android versions prior to 3.0
    }

    public void surfaceCreated(SurfaceHolder holder) {
        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();
        } 
        catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        }
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        camera.stopPreview();
        camera.release();
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
        // 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 (surfaceHolder.getSurface() == null){
            return;
        }
        try {
            camera.stopPreview();
        } 
        catch (Exception e){
            // ignore: tried to stop a non-existent preview
        }

        // set preview size and make any resize, rotate or
        // reformatting changes here
        camera.setDisplayOrientation(90);
        // start preview with new settings
        try {
            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
        } 
        catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }
}
摄像活动

public class CameraActivity extends SherlockActivity {

    private static final String TAG = "CameraActivity";
    public static final int MEDIA_TYPE_IMAGE = 1;
    public static final int MEDIA_TYPE_VIDEO = 2;
    private Camera camera;
    private CameraPreview preview;
    private PictureCallback picture = new PictureCallback() { 
        @Override
        public void onPictureTaken(byte[] data, Camera camera) {
            File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
            if (pictureFile == null){
                Log.d(TAG, "Error creating media file, check storage permissions: media file is null.");
                return;
            }
            try {
                FileOutputStream fos = new FileOutputStream(pictureFile);
                fos.write(data);
                fos.close();
            } 
            catch (FileNotFoundException e) {
                Log.d(TAG, "File not found: " + e.getMessage());
            } 
            catch (IOException e) {
                Log.d(TAG, "Error accessing file: " + e.getMessage());
            }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getSupportActionBar().hide();
        setContentView(R.layout.camera_view);

        /**Get a camera instance**/
        if(CameraManager.doesDeviceHaveCamera(this)){
            camera = CameraManager.getCameraInstance();
        }
        else{
            return;
        }

        /**Create a SurfaceView preview (holds the camera feed) and set it to the corresponding XML layout**/
        preview = new CameraPreview(this, camera);
        FrameLayout previewContainer = (FrameLayout) findViewById(R.id.camera_preview);
        previewContainer.addView(preview);

        /**Set up a capture button, which takes the picture**/
        Button captureButton = (Button) findViewById(R.id.button_capture);
        captureButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                camera.takePicture(null, null, picture);
            }
        });
    }

    /** Create a File for saving an image or video */
    private static File getOutputMediaFile(int type){
        // To be safe, you should check that the SDCard is mounted
        // using Environment.getExternalStorageState() before doing this.

        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "MyDirectory");
        // This location works best if you want the created images to be shared
        // between applications and persist after your app has been uninstalled.

        // Create the storage directory if it does not exist
        if (! mediaStorageDir.exists()){
            if (! mediaStorageDir.mkdirs()){
                Log.d("MyDirectory", "failed to create directory");
                return null;
            }
        }
        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
        File mediaFile;
        if (type == MEDIA_TYPE_IMAGE){
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "IMG_"+ timeStamp + ".jpg");
        } 
        else if(type == MEDIA_TYPE_VIDEO) {
            mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                    "VID_"+ timeStamp + ".mp4");
        } 
        else {
            return null;
        }
        return mediaFile;
    }
}
我通过主活动屏幕上的按钮调用CameraActivity

当我拍摄一张照片并调用onPictureTaken()时,问题就出现了:它要么不工作,要么保存一张照片需要花费相当长的时间

它是一个大文件,您正在主应用程序线程上写入它

本质上,我只想访问原始字节数组,而不必保存它

那就不要保存它。小心地使用静态数据成员将字节数组放在主活动可以使用的地方(并且,当您使用它时,
null
将静态引用移出,以便可以对内存进行垃圾收集)。

如果您这样做,您的
pictureTaken()
回调将在该线程上调用,而不会妨碍UI响应。请注意,您仍然可以从
onClick()
调用
camera.takePicture()
,即从UI线程调用


这仍然不是一个灵丹妙药:您可能希望将写入文件卸载到另一个工作线程,以便让相机拍摄下一张照片。如上所述,如果您只需要实时访问原始字节数组,请不要写入文件。

因此我应该在主活动(调用CameraActivity的活动)中的异步任务上启动CameraActivity。@yoaquim:我不知道“调用CameraActivity”是什么意思。如果您的意思是
startActivity()
,那么在后台线程上调用
startActivity()
是没有意义的。已编辑,谢谢(我的意思是启动CameraActivity)。那么,在CameraActivity中,我应该在一个单独的线程中写入文件?@yoaquim:这肯定会阻止您的UI在写入磁盘时冻结。一般来说,磁盘和网络I/O应该总是在后台线程上完成。是的,在我这方面,这是一个愚蠢的错误。实际上,我曾想过像Mark所说的那样将字节数组保存在静态字段中,但我有了第二个想法,因为它们需要非常小心。唉,我就是这么做的,而且效果很好。谢谢