从 MainActivity 使用 Camera API SurfaceView 直接打开 activity 时出错

Error direclty opening activity with Camera API SurfaceView from MainActivity

我有一个 MainActivity,仅当 phone 号码未在共享首选项中设置时才使用。在不需要 MainActivity 并且每次用户打开应用程序时设置 phone 数字后,他应该转到 CameraActivity Activity ,其中 Camera API 在 SurfaceView 中工作在里面。

使用 MainActivity 上的按钮打开 CameraActivity 没有问题,但是如果我从 MainACtivity onCreate 方法打开 CameraActivity,那么我将面临以下错误:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.hardware.Camera.setDisplayOrientation(int)' on a null object reference
            at com.vivertechnologies.camera.ShowCamera.refreshCamera(ShowCamera.java:208)
            at com.vivertechnologies.camera.ShowCamera.surfaceChanged(ShowCamera.java:281)
            at android.view.SurfaceView.updateWindow(SurfaceView.java:590)
            at android.view.SurfaceView.onPreDraw(SurfaceView.java:176)
            at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:847)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1983)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1081)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5818)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
            at android.view.Choreographer.doCallbacks(Choreographer.java:580)
            at android.view.Choreographer.doFrame(Choreographer.java:550)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:135)
            at android.app.ActivityThread.main(ActivityThread.java:5234)
            at java.lang.reflect.Method.invoke(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:372)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:909)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:704)

我的相机代码 API 是:

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.camera_show);

        Thread t = new Thread() {

            @Override
            public void run() {
                try {
                    while (!isInterrupted()) {
                        Thread.sleep(1000);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                // update TextView here!
                                Calendar c = Calendar.getInstance();
                                SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
                                String formattedDate = df.format(c.getTime());
                                TextView txtView = (TextView)findViewById(R.id.dateTime);
                                txtView.setText(formattedDate);
                            }
                        });
                    }
                } catch (InterruptedException e) {
                }
            }
        };

        t.start();

        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        surfaceHolder = surfaceView.getHolder();

        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        jpegCallback = new PictureCallback() {

            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                new Thread(new SavePicThread(data)).start();
//
//                Intent i = new Intent(ShowCamera.this, UploadActivity.class);
//                i.putExtra("isImage", data);
//                startActivity(i);
                refreshCamera();
            }
        };
    }

正在生成错误的刷新相机方法:

public void refreshCamera() {
        if (surfaceHolder.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
        }

        int rotation = ((WindowManager)ShowCamera.this.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
        int degrees = 0;

        // specifically for back facing camera
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 90;
                break;
            case Surface.ROTATION_90:
                degrees = 0;
                break;
            case Surface.ROTATION_180:
                degrees = 270;
                break;
            case Surface.ROTATION_270:
                degrees = 180;
                break;
        }


        camera.setDisplayOrientation(degrees);
        setCamera(camera);
        try {
            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
        } catch (Exception e) {
            Log.d("AAAAA", "Error starting camera preview: " + e.getMessage());
        }
    }

我无法确定如何解决它。

编辑:

完整代码:

    package com.vivertechnologies.camera;


import android.content.Context;
import android.content.Intent;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Menu;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * Created by usman on 27/11/15.
 */
public class ShowCamera extends MainActivity implements SurfaceHolder.Callback {
    Camera camera;
    SurfaceView surfaceView;
    SurfaceHolder surfaceHolder;

    Camera.PictureCallback rawCallback;
    Camera.ShutterCallback shutterCallback;
    Camera.PictureCallback jpegCallback;


    private Camera.Size mPreviewSize;
    private List<Camera.Size> mSupportedPreviewSizes;
    private Context mContext;
    private SurfaceView mSurfaceView;
    private SurfaceHolder mHolder;
    private final String TAG = "CameraSurfaceView";
    private Camera mCamera;
    private List<String> mSupportedFlashModes;

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

        Thread t = new Thread() {

            @Override
            public void run() {
                try {
                    while (!isInterrupted()) {
                        Thread.sleep(1000);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                // update TextView here!
                                Calendar c = Calendar.getInstance();
                                SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
                                String formattedDate = df.format(c.getTime());
                                TextView txtView = (TextView)findViewById(R.id.dateTime);
                                txtView.setText(formattedDate);
                            }
                        });
                    }
                } catch (InterruptedException e) {
                }
            }
        };

        t.start();

        surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
        surfaceHolder = surfaceView.getHolder();

        surfaceHolder.addCallback(this);
        surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

        jpegCallback = new PictureCallback() {

            @Override
            public void onPictureTaken(byte[] data, Camera camera) {
                new Thread(new SavePicThread(data)).start();
//
//                Intent i = new Intent(ShowCamera.this, UploadActivity.class);
//                i.putExtra("isImage", data);
//                startActivity(i);
                refreshCamera();
            }
        };
    }

    private void galleryAddPic(File file) {
        Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        Uri contentUri = Uri.fromFile(file);
        mediaScanIntent.setData(contentUri);
        ShowCamera.this.sendBroadcast(mediaScanIntent);
    }


    public class SavePicThread implements Runnable {
        byte[] data;
        public SavePicThread(byte[] data) {
            this.data = data;
        }
        public void run() {
            // make a new picture file
            File pictureFile = getOutputMediaFile();

            if (pictureFile == null) {
                return;
            }
            try {
                // write to the file
                FileOutputStream fos = new FileOutputStream(pictureFile);
                fos.write(data);
                fos.flush();
                fos.close();
                ShowCamera.this.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
//                        Toast toast = Toast.makeText(ShowCamera.this, "Picture saved", Toast.LENGTH_SHORT);
//                        toast.show();
                    }
                });
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            // make the picture visible to the rest of the device
            galleryAddPic(pictureFile);
            Intent i = new Intent(ShowCamera.this, UploadActivity.class);
            i.putExtra("filePath", pictureFile.getPath());
            startActivity(i);
        }
    }

    // make picture and save to a folder
    private File getOutputMediaFile() {
        // make a new file directory inside the "sdcard" folder
        File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), "imgCaptureApp");

        // if the directory does not exist
        if (!mediaStorageDir.exists()) {
            // if you cannot make this directory return
            if (!mediaStorageDir.mkdirs()) {
                return null;
            }
        }

        // take the current timeStamp
        String timeStamp = new SimpleDateFormat("dd-MM-yyyy_HH:mm:ss").format(new Date());
        File mediaFile;
        // and make a media file:
        mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");

        return mediaFile;
    }



    public void captureImage(View v) throws IOException {
        camera.takePicture(null, null, jpegCallback);
    }

    public void refreshCamera() {
        if (surfaceHolder.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
        }

        int rotation = ((WindowManager)ShowCamera.this.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
        int degrees = 0;

        // specifically for back facing camera
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 90;
                break;
            case Surface.ROTATION_90:
                degrees = 0;
                break;
            case Surface.ROTATION_180:
                degrees = 270;
                break;
            case Surface.ROTATION_270:
                degrees = 180;
                break;
        }


        camera.setDisplayOrientation(degrees);
        setCamera(camera);
        try {
            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
        } catch (Exception e) {
            Log.d("AAAAA", "Error starting camera preview: " + e.getMessage());
        }
    }

    public void setCamera(Camera camera)
    {
        Camera mCamera = camera;
        if (mCamera != null)
        {
            mSupportedPreviewSizes = mCamera.getParameters().getSupportedPreviewSizes();
            mSupportedFlashModes = mCamera.getParameters().getSupportedFlashModes();
            // Set the camera to Auto Flash mode.
            if (mSupportedFlashModes.contains(Camera.Parameters.FLASH_MODE_AUTO))
            {
                Camera.Parameters parameters = mCamera.getParameters();
                parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
                mCamera.setParameters(parameters);
            }
        }
        surfaceView.requestLayout();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            camera = Camera.open();
        }

        catch (RuntimeException e) {
            System.err.println(e);
            return;
        }

        Camera.Parameters param;
        param = camera.getParameters();

        List<Camera.Size> sizes = param.getSupportedPictureSizes();
        Camera.Size size = sizes.get(0);
        for(int i=0;i<sizes.size();i++)
        {
            if(sizes.get(i).width > size.width)
                size = sizes.get(i);
        }
        param.setPictureSize(size.width, size.height);
        param.setRotation(90);
        param.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
//        param.setPreviewSize(352, 288);
        camera.setParameters(param);

        try {
            camera.setPreviewDisplay(surfaceHolder);
            camera.startPreview();
        }

        catch (Exception e) {
            System.err.println(e);
            return;
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        refreshCamera();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        camera.stopPreview();
        camera.release();
        camera = null;
    }
}

根据您的描述,您的问题似乎是 ShowCameraMainActivity 的子class。据推测,您在 MainActivityonCreate() 方法中有类似于以下内容的内容:

SharedPreferences prefs = ...

if(prefs.getBoolean("phone_number_set", false)) {
    startActivity(new Intent(this, ShowCamera.class));
}

由于 ShowCameraMainActivity 的子 class,因此在 ShowCameraonCreate() 方法中调用 super.onCreate(savedInstanceState); 正在执行 MainActivityonCreate() 方法再次出现。当你用 Button 打开 ShowCamera 时,你还没有设置 phone 数字,所以上面显示的 if 条件是 falsestartActivity() 方法不再被调用。

然而,在你设置了 phone 号码后,条件是 true,它再次开始 ShowCamera,这就是调用 MainActivityonCreate(),执行 if 块,启动 ShowCamera,调用 MainActivityonCreate(),执行 if 块,启动ShowCamera... 这很容易验证,方法是在 ShowCamera class 中添加一个 static int,将其打印到登录 onCreate(),然后立即递增.

最终,ShowCamera 实例之一将布局其 SurfaceView 并从 surfaceChanged() 回调中调用 refreshCamera(),但它将有一个空值 Camera 对象,因为之前的 Activity 已经有 camera.open,它会抛出你得到的 NullPointerException

解决方法很简单。将 ShowCamera class 更改为 Activity 的直接子 class。即:

public class ShowCamera extends Activity implements SurfaceHolder.Callback {