Android Camera.takePicture失败了

时间:2014-01-29 20:05:51

标签: java android api camera

作为一个学习练习,我正在编写一个安全应用程序,当任意事件发生时,需要打开相机,拍照并关闭相机,而不必担心闪光灯,焦点,或显示预览。我跟着在线演示并制作了一个可以拍照的工作应用程序,但它使用预览和所有这些。所以我开始努力让它在没有预览的情况下工作。无论如何,我不断得到'takePicture failed'例外,我没有任何世俗的想法。我希望有更多使用Camera API经验的人可以看看并指出我的解决方案。以下是我的相关文件。我正在使用最新的Android Studio并在Galaxy S4上进行测试。

[MainActivity.java]

package com.g5digital.cam2;

import android.content.pm.PackageManager;
import android.hardware.Camera;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

import java.io.IOException;

public class MainActivity extends ActionBarActivity implements View.OnClickListener {

    private static final String TAG = "MainActivity";
    private Button button;
    private int cameraId;
    private Camera camera;
    private CameraPreview camPreview;
    private LinearLayout container;
    private Camera.Parameters camParms;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        container = (LinearLayout)findViewById(R.id.container);

        button = (Button)findViewById(R.id.button);
        button.setOnClickListener(this);
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onClick(View view) {
        openCamera();

        try {
            if (camera != null) {
                PhotoHandler ph = new PhotoHandler(this, camera);
                camera.takePicture(null, null, ph);

                // Commented until takePicture() works
                /*(new Handler()).postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        MainActivity.this.closeCamera();
                    }
                }, 1000);*/
            }
        }
        catch (Exception e) {
            closeCamera();
            Log.d(TAG, e.getMessage());
            e.printStackTrace();
        }
    }

    private void openCamera() {
        // do we have a camera?
        if (!getPackageManager()
                .hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
            Toast.makeText(this, "No camera on this device", Toast.LENGTH_LONG)
                    .show();
        }
        else {
            closeCamera();

            cameraId = findFrontFacingCameraId();
            if (cameraId < 0) {
                Toast.makeText(this, "No front facing camera found.",
                        Toast.LENGTH_LONG).show();
            } else {
                camera = Camera.open(cameraId);
                try {
                    setCamParms();

                    setCamPreview();
                    camera.startPreview();
                }
                catch (Exception e) {
                    closeCamera();
                    e.printStackTrace();
                    finish();
                    return;
                }
            }
        }
    }

    private void closeCamera() {
        if (camera != null) {
            camera.release();
            camera = null;
        }
    }

    private int findFrontFacingCameraId() {
        int camera_id = -1;
        // Search for the front facing camera
        int numberOfCameras = Camera.getNumberOfCameras();
        for (int i = 0; i < numberOfCameras; i++) {
            Camera.CameraInfo info = new Camera.CameraInfo();
            Camera.getCameraInfo(i, info);
            if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                Log.d(TAG, "Camera found");
                camera_id = i;
                break;
            }
        }
        return camera_id;
    }

    private void setCamParms() {
        if (camParms == null && camera != null) {
            camParms = camera.getParameters();
            camParms.setFlashMode("Off");
        }
        if (camera != null) {
            camera.setParameters(camParms);
            camera.setDisplayOrientation(90);
        }
    }

    private void setCamPreview() throws IOException {
        if (camPreview == null && camera != null) {
            camPreview = new CameraPreview(this, camera);
        }
        if (camera != null) {
            camera.setPreviewDisplay(camPreview.getHolder());
        }
    }
}

[CameraPreview.java]

package com.g5digital.cam2;

import android.content.Context;
import android.hardware.Camera;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.io.IOException;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
    private Context context;
    private Camera camera;
    private SurfaceHolder holder;
    private static final String TAG = "CameraPreview";

    public CameraPreview(Context c, Camera cam) {
        super(c);
        context = c;
        camera = cam;
        holder = getHolder();
        holder.addCallback(this);
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();
        } catch (IOException e) {
            Log.d(TAG, "Error setting camera preview: " + e.getMessage());
        } catch (Exception e) {
            // Probably getting "called after release()" message
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i2, int i3) {
        // 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 (holder.getSurface() == null){
            // preview surface does not exist
            return;
        }

        // stop preview before making changes
        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

        // start preview with new settings
        try {
            camera.setPreviewDisplay(holder);
            camera.startPreview();

        } catch (Exception e){
            Log.d(TAG, "Error starting camera preview: " + e.getMessage());
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        //
    }
}

[PhotoHandler.java]

package com.g5digital.cam2;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.hardware.Camera;
import android.os.Environment;
import android.util.Log;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class PhotoHandler implements Camera.PictureCallback {
    private final Context context;
    private final Camera camera;

    public PhotoHandler(Context context, Camera c) {
        this.context = context;
        this.camera = c;
    }

    @Override
    public void onPictureTaken(byte[] bytes, Camera cam) {
        Log.i("PhotoHandler", "Picture taken!");
        File pictureFileDir = getDir();

        if (!pictureFileDir.exists() && !pictureFileDir.mkdirs()) {

            Log.d("PhotoHandler", "Can't create directory to save image.");
            Toast.makeText(context, "Can't create directory to save image.",
                    Toast.LENGTH_LONG).show();
            return;

        }

        Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        int width = bmp.getWidth();
        int height = bmp.getHeight();
        Matrix matrix = new Matrix();
        matrix.postRotate(270);
        Bitmap rotatedBitmap = Bitmap.createBitmap(bmp, 0, 0,
                width, height, matrix, true);

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String date = dateFormat.format(new Date());
        String photoFile = "Picture_" + date + ".jpg";

        String filename = pictureFileDir.getPath() + File.separator + photoFile;

        File pictureFile = new File(filename);

        try {
            FileOutputStream fos = new FileOutputStream(pictureFile);
            boolean result = rotatedBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
            //fos.write(bytes);
            fos.close();
            if (result) {
                Toast.makeText(context, "New Image saved:" + photoFile,
                        Toast.LENGTH_LONG).show();
            }
            else {
                Toast.makeText(context, "Couldn't save image:" + photoFile,
                        Toast.LENGTH_LONG).show();
            }
            camera.startPreview();
        } catch (Exception error) {
            Log.d("PhotoHandler", "File" + filename + "not saved: "
                    + error.getMessage());
            Toast.makeText(context, "Image could not be saved.",
                    Toast.LENGTH_LONG).show();
        }
    }

    private File getDir() {
        File sdDir = Environment
                .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
        return new File(sdDir, "CameraAPIDemo");
    }
}

[AndroidManifest.xml中]

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.g5digital.cam2" >

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.FLASHLIGHT" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.g5digital.cam2.MainActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

[activity_main.xml中]

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.g5digital.cam2.MainActivity"
    tools:ignore="MergeRootFrame">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/click"
        android:id="@+id/button" />
</LinearLayout>

[LogCat输出]

01-29 14:55:45.826    5853-5853/com.g5digital.cam2 D/MainActivity﹕ takePicture failed
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ java.lang.RuntimeException: takePicture failed
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.hardware.Camera.native_takePicture(Native Method)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.hardware.Camera.takePicture(Camera.java:1194)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.hardware.Camera.takePicture(Camera.java:1139)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at com.g5digital.cam2.MainActivity.onClick(MainActivity.java:66)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.view.View.performClick(View.java:4475)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.view.View$PerformClick.run(View.java:18786)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.os.Handler.handleCallback(Handler.java:730)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.os.Handler.dispatchMessage(Handler.java:92)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.os.Looper.loop(Looper.java:137)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at android.app.ActivityThread.main(ActivityThread.java:5419)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at java.lang.reflect.Method.invokeNative(Native Method)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at java.lang.reflect.Method.invoke(Method.java:525)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)
01-29 14:55:45.826    5853-5853/com.g5digital.cam2 W/System.err﹕ at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
01-29 14:55:45.836    5853-5853/com.g5digital.cam2 W/System.err﹕ at dalvik.system.NativeStart.main(Native Method)

1 个答案:

答案 0 :(得分:3)

不,要拍照,必须显示预览。有技巧的人发明了很多解决方法,例如, Take Picture without preview Android,  Android: Is it possible to take a picture with the camera from a service with no UIHow to use Camera to take picture in a background Service on Android? ...

但请记住,要求不是出于技术要求,而是出于隐私目的。系统继续发展并防范新发现的变通方法。

在S4上隐藏预览最强大的方法可能是使用SurfaceTexture,但不可见地显示它,例如转移了视口。