1

我的主要活动展示了相机预览。当我旋转手机时,应用程序关闭。在日志中,我看到在旋转后从不调用 surfaceCreated() 和 surfaceChanged(),然后调用 main 的 onStop()。

摩托罗拉和三星手机表现出相同的行为。我的应用程序中的其他活动按预期在轮换中存活。

我认为问题可能是过时的预览参考,但日志显示新的持有者和预览实例。

  1. 为什么轮换后不调用持有者回调?
  2. 我可以强迫他们打电话吗?

日志片段

07-27 08:32:12.093: I/MainActivity(21285):   
07-27 08:32:12.093: I/MainActivity(21285): ***********STARTING APP*****************
         07-27 08:32:12.093: I/MainActivity(21285): onCreate
07-27 08:32:12.093: I/MainActivity(21285):   
07-27 08:32:12.289: I/StackOPreview(21285): in constructor
07-27 08:32:12.289: V/StackOPreview(21285): holder: android.view.SurfaceView$3@4053af90
07-27 08:32:12.289: V/StackOPreview(21285): this: com.chex.camera.StackOPreview@4053aba0
07-27 08:32:12.296: I/MainActivity(21285): onCreate finished
07-27 08:32:12.296: I/MainActivity(21285):  
07-27 08:32:12.296: I/MainActivity(21285): |||||||||||||||||||   ON RESUME  |||||||||||||||||||
07-27 08:32:12.296: I/MainActivity(21285):  
07-27 08:32:12.367: I/StackOPreview(21285): surfaceCreated
07-27 08:32:12.375: I/StackOPreview(21285): view width: 540  height: 960
07-27 08:32:12.375: I/StackOPreview(21285): portrait mode so rotate camera preview
07-27 08:32:12.382: I/CameraSettings(21285): retreiving portrait orientation
07-27 08:32:12.382: I/SettingsCP(21285):  starting Settings ContentProvider query 
07-27 08:32:12.421: I/SettingsCP(21285): got 1 settings
07-27 08:32:12.421: I/CameraSettings(21285):  retrieved portrait orientation= 90
07-27 08:32:12.429: I/CameraSettings(21285):  
07-27 08:32:12.429: I/CameraSettings(21285): retreiving portrait rotation
07-27 08:32:12.429: I/SettingsCP(21285):  starting Settings ContentProvider query 
07-27 08:32:12.429: I/SettingsCP(21285): got 1 settings
07-27 08:32:12.429: I/CameraSettings(21285):  retrieved portrait rotation= 90
07-27 08:32:12.429: I/CameraSettings(21285):  
07-27 08:32:12.468: I/StackOPreview(21285): setting View measured dimensions to width: 540  height: 960
07-27 08:32:12.468: I/StackOPreview(21285): optimal preview width: 1920  height: 1080
07-27 08:32:13.929: I/StackOPreview(21285): surfaceChanged
07-27 08:32:14.085: I/StackOPreview(21285): view width: 540  height: 960
07-27 08:32:14.085: I/StackOPreview(21285): portrait mode so rotate camera preview
07-27 08:32:14.085: I/CameraSettings(21285): retreiving portrait orientation
07-27 08:32:14.085: I/SettingsCP(21285):  starting Settings ContentProvider query 
07-27 08:32:14.085: I/SettingsCP(21285): got 1 settings
07-27 08:32:14.085: I/CameraSettings(21285):  retrieved portrait orientation= 90
07-27 08:32:14.085: I/CameraSettings(21285):  
07-27 08:32:14.085: I/CameraSettings(21285): retreiving portrait rotation
07-27 08:32:14.085: I/SettingsCP(21285):  starting Settings ContentProvider query 
07-27 08:32:14.085: I/SettingsCP(21285): got 1 settings
07-27 08:32:14.093: I/CameraSettings(21285):  retrieved portrait rotation= 90
07-27 08:32:14.093: I/CameraSettings(21285):  
07-27 08:32:14.101: I/StackOPreview(21285): setting View measured dimensions to width: 540  height: 960
07-27 08:32:14.101: I/StackOPreview(21285): optimal preview width: 1920  height: 1080
07-27 08:32:27.109: I/MainActivity(21285):  
07-27 08:32:27.109: I/MainActivity(21285): |||||||||||||||||||   ON PAUSE  |||||||||||||||||||
07-27 08:32:27.109: I/MainActivity(21285):  
07-27 08:32:27.757: I/StackOPreview(21285): onPause
07-27 08:32:27.757: I/MainActivity(21285):  releasing camera in onPause
07-27 08:32:27.757: E/MainActivity(21285): onStop
07-27 08:32:27.757: I/MainActivity(21285):  
07-27 08:32:27.757: I/MainActivity(21285): |||||||||||||||||||   ON STOP  |||||||||||||||||||

这是旋转开始

07-27 08:32:27.757: I/MainActivity(21285):  
07-27 08:32:27.789: I/MainActivity(21285):   
07-27 08:32:27.789: I/MainActivity(21285): ***********STARTING APP*****************
07-27 08:32:27.789: I/MainActivity(21285): onCreate
07-27 08:32:27.789: I/MainActivity(21285):   
07-27 08:32:27.890: I/StackOPreview(21285): in constructor
07-27 08:32:27.890: V/StackOPreview(21285): holder: android.view.SurfaceView$3@40566d20
07-27 08:32:27.890: V/StackOPreview(21285): this: com.chex.camera.StackOPreview@40566930
07-27 08:32:27.898: I/MainActivity(21285): onCreate finished
07-27 08:32:27.898: I/MainActivity(21285):  
07-27 08:32:27.898: I/MainActivity(21285): |||||||||||||||||||   ON RESUME  |||||||||||||||||||


HERE IS WHERE surfaceCreated SHOULD BE CALLED

07-27 08:32:27.898: I/MainActivity(21285):  
07-27 08:32:27.945: I/MainActivity(21285):  
07-27 08:32:27.945: I/MainActivity(21285): |||||||||||||||||||   ON PAUSE  |||||||||||||||||||
07-27 08:32:27.945: I/MainActivity(21285):  
07-27 08:32:27.968: I/StackOPreview(21285): onPause
07-27 08:32:27.968: I/MainActivity(21285):  releasing camera in onPause
07-27 08:32:28.562: E/MainActivity(21285): onStop

由 Launcher 调用的 Main

public class MainActivity extends Activity implements Constants

{

    private static final String TAG = "MainActivity";

    // CameraView variables
    Camera camera;

    boolean isPreviewRunning = false;

    StackOPreview preview;

    PhotoSaver photoSaver = null;

    LayoutInflater controlInflater = null;

    // first rear facing camera
    int defaultCameraId;

    // can't put big byte array in an Intent so keep a reference. This may not
    // survive pauses.
    private static byte[] currentPhoto = null;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Log.i(TAG, "  ");
        Log.i(TAG, "***********STARTING APP*****************");

        Log.i(TAG, "onCreate");

        Log.i(TAG, "  ");

        getWindow().setFormat(PixelFormat.TRANSLUCENT);

        requestWindowFeature(Window.FEATURE_NO_TITLE);

        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        // Find the total number of cameras available
        int numberOfCameras = Camera.getNumberOfCameras();

        // Find the ID of the default camera. This assumes the FACING BACK
        // camera is default. probably burns us sometime.
        CameraInfo cameraInfo = new CameraInfo();
        for (int i = 0; i < numberOfCameras; i++) {
            Camera.getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
                defaultCameraId = i;
            }
        }

        camera = Camera.open(defaultCameraId);
        preview = new StackOPreview(this, camera);
        setContentView(preview);

        Log.i(TAG, "onCreate finished");

    }

    protected void onResume() {

        super.onResume();

        Log.i(TAG, " ");
        Log.i(TAG, "|||||||||||||||||||   ON RESUME  |||||||||||||||||||");
        Log.i(TAG, " ");

        if (camera == null) {
            // camera is rarely null in onResume. Either app was destroyed then
            // recreated because a long time passed, or app was just paused and
            // camera is still valid.
            camera = Camera.open(defaultCameraId);

            // July 24, 2013 sometimes preview is black - suspect it doesn't
            // have a camera
            preview.onResume(camera);

            Log.i(TAG, "OPENING camera in onResume");
        }

    }

    protected void onPause() {
        super.onPause();

        Log.i(TAG, " ");
        Log.i(TAG, "|||||||||||||||||||   ON PAUSE  |||||||||||||||||||");
        Log.i(TAG, " ");

        // Release camera when activity paused
        if (camera != null) {
            camera.stopPreview();
            camera.release();
            camera = null;

            preview.onPause();

            Log.i(TAG, " releasing camera in onPause");
        }

        // close the database
        Context context = getApplicationContext();
        DatabaseHelper databaseHelper = DatabaseHelper.getInstance(context);
        databaseHelper.close();
    }

    protected void onStop() {
        Log.e(TAG, "onStop");
        super.onStop();

        Log.i(TAG, " ");
        Log.i(TAG, "|||||||||||||||||||   ON STOP  |||||||||||||||||||");
        Log.i(TAG, " ");

        // close the database
        Context context = getApplicationContext();
        DatabaseHelper databaseHelper = DatabaseHelper.getInstance(context);
        databaseHelper.close();

        finish();
    }

    /** takes the picture */
    public void onClick(View arg0) {
        Log.e(TAG, "in CameraView.onClick");
        camera.takePicture(null, null, photoSaver);

    }

    /**
     * @return the currentPhoto
     */
    public static byte[] getCurrentPhoto() {
        return currentPhoto;
    }

    /**
     * @param currentPhoto
     *            the currentPhoto to set
     */
    public void setCurrentPhoto(byte[] currentPhoto) {
        this.currentPhoto = currentPhoto;
    }
  }

预览代码:

   public class StackOPreview extends SurfaceView implements
    SurfaceHolder.Callback {

private SurfaceHolder mHolder;
public Camera mCamera;

private static boolean DEBUGGING = true;
private static final String LOG_TAG = "StackOPreview";
private static final String CAMERA_PARAM_ORIENTATION = "orientation";
private static final String CAMERA_PARAM_LANDSCAPE = "landscape";
private static final String CAMERA_PARAM_PORTRAIT = "portrait";
protected Activity mActivity;

protected List<Camera.Size> previewSizeList = null;

protected Camera.Size previewSize = null;

public StackOPreview(Context context, Camera camera) {
    super(context);

    Log.i(LOG_TAG,"in constructor");

    mActivity = (Activity) context;
    mCamera = camera;

    // Install a SurfaceHolder.Callback so we get notified when the
    // underlying surface is created and destroyed.
    mHolder = getHolder();
    mHolder.addCallback(this);

    Log.v(LOG_TAG,"holder: "+mHolder);
    Log.v(LOG_TAG,"this: "+ this);

    // deprecated setting, but required on Android versions prior to 3.0
    mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

}

public void surfaceCreated(SurfaceHolder holder) {
    Log.i(LOG_TAG, "surfaceCreated");
    // The Surface has been created, now tell the camera where to draw the
    // preview.
    try {

        if (mCamera != null) {

            setRotationAndResolution();

            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();

        } else {
            Log.d(LOG_TAG, " camera is null");
        }
    } catch (IOException e) {
        Log.d("CameraView",
                "Error setting camera preview: " + e.getMessage());
    }
}

public void surfaceDestroyed(SurfaceHolder holder) {
    Log.i(LOG_TAG, "surfaceDestroyed");
    // empty. Take care of releasing the Camera preview in your activity.
}

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    Log.i(LOG_TAG, "surfaceChanged");
    // 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 (mHolder.getSurface() == null) {
        Log.d(LOG_TAG, " mHolder is null");
        // preview surface does not exist
        return;
    }

    if (mCamera == null) {
        Log.d(LOG_TAG, " camera is null");

        return;
    }

    // stop preview before making changes
    try {
        mCamera.stopPreview();
    } catch (Exception e) {
        // ignore: tried to stop a non-existent preview
        Log.e(LOG_TAG, "failed to stopPreview because " + e, e);
    }

    // start preview with new settings
    try {
        setRotationAndResolution();

        mCamera.setPreviewDisplay(mHolder);
        mCamera.startPreview();

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

public void onPause() {
    Log.i(LOG_TAG, "onPause");
    if (null == mCamera) {
        return;
    }

    mCamera = null;

    // try releasing
    mHolder.removeCallback(this);
}

// added July 24 2013.
/**
 * Rarely called because camera is rarely null in MainActivity's onResume().
 * Either app was destroyed then recreated because a long time passed, or
 * app was just paused and camera is still valid. Occasionally I see a black
 * screen after sleeping for a while so I added this method to try to fix
 * it.
 */
public void onResume(Camera camera) {
    Log.i(LOG_TAG, "onResume");
    mCamera = camera;
}

private void setRotationAndResolution() {
    final int width = getWidth();
    final int height = getHeight();

    Log.i(LOG_TAG, "view width: " + width + "  height: " + height);

    if (height > width) {
        // PORTRAIT

        Log.i(LOG_TAG, "portrait mode so rotate camera preview");

        Context context = getContext();

        int portraitOrientation = CameraSettings
                .getPortraitPreviewOrientation(context);

        int portraitRotation = CameraSettings
                .getPortraitPictureRotation(context);

        try {
            // THis line fixed the preview orientation. seems to
            // have to be called before setPreviewDisplay()
            mCamera.setDisplayOrientation(portraitOrientation);

            Camera.Parameters parameters = mCamera.getParameters();

            // this line fixes the recorded image's orientation
            parameters.setRotation(portraitRotation);

            previewSize = getOptimalPreviewSize();

            parameters
                    .setPreviewSize(previewSize.width, previewSize.height);

            mCamera.setParameters(parameters);
        } catch (Exception e) {
            Log.e(LOG_TAG, "portrait preview settings failed: " + e, e);
            Log.e(LOG_TAG, "  orientation: " + portraitOrientation);
            Log.e(LOG_TAG, "  rotation: " + portraitRotation);
            Log.e(LOG_TAG, " ");
        }

    } else {
        // LANDSCAPE

        Log.i(LOG_TAG, " landscape mode");

        int landscapeOrientation = 0;
        int landscapeRotation = 0;

        try {
            Context context = getContext();
            landscapeOrientation = CameraSettings
                    .getLandscapePreviewOrientation(context);
            landscapeRotation = CameraSettings
                    .getLandscapePictureRotation(context);

            mCamera.setDisplayOrientation(landscapeOrientation);

            previewSize = getOptimalPreviewSize();

            // THis line fixed the preview orientation. seems to
            // have to be called before setPreviewDisplay()
            mCamera.setDisplayOrientation(landscapeOrientation);

            Camera.Parameters parameters = mCamera.getParameters();

            // this line fixes the recorded image's orientation
            parameters.setRotation(landscapeRotation);

            parameters
                    .setPreviewSize(previewSize.width, previewSize.height);

            mCamera.setParameters(parameters);
        } catch (Exception e) {
            Log.e(LOG_TAG, "landscape preview settings failed: " + e, e);
            Log.e(LOG_TAG, "  orientation: " + landscapeOrientation);
            Log.e(LOG_TAG, "  rotation: " + landscapeRotation);
            Log.e(LOG_TAG, " ");
        }
    }

}

private Size getOptimalPreviewSize() {

    // if list isn't retrieved yet, then get it
    if (previewSizeList == null) {
        previewSizeList = mCamera.getParameters()
                .getSupportedPreviewSizes();

    }

    int width = getWidth();

    int height = getHeight();

    Log.i(LOG_TAG, "setting View measured dimensions to width: " + width
            + "  height: " + height);

    final double ASPECT_TOLERANCE = 0.1;
    double targetRatio = (double) width / height;

    Size optimalSize = null;
    double minDiff = Double.MAX_VALUE;

    int targetHeight = height;

    // Try to find an size match aspect ratio and size
    for (Size size : previewSizeList) {
        //Log.v(LOG_TAG, "  width: " + size.width + "  height: "
            //  + size.height);

        double ratio = (double) size.width / size.height;
        if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
            continue;
        if (Math.abs(size.height - targetHeight) < minDiff) {
            optimalSize = size;
            minDiff = Math.abs(size.height - targetHeight);
        }
    }

    // Cannot find the one match the aspect ratio, ignore the requirement
    if (optimalSize == null) {
        minDiff = Double.MAX_VALUE;
        for (Size size : previewSizeList) {
            if (Math.abs(size.height - targetHeight) < minDiff) {
                optimalSize = size;
                minDiff = Math.abs(size.height - targetHeight);
            }
        }
    }

    Log.i(LOG_TAG, "optimal preview width: " + optimalSize.width
            + "  height: " + optimalSize.height);

    return optimalSize;
}

}

4

1 回答 1

1

我使用 Commonsware 的 Mark Murphy 的提示解决了这个问题。我在主要活动中将 Preview create 移至 onResume() 。此外,我删除了 onStop() 中的 finish() 调用。这是 MainActivity 中的 onCreate() 和 onResume() 工作:

public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Find the total number of cameras available
        int numberOfCameras = Camera.getNumberOfCameras();

        // Find the ID of the default camera. This assumes the FACING BACK
        // camera is default. probably burns us sometime.
        CameraInfo cameraInfo = new CameraInfo();
        for (int i = 0; i < numberOfCameras; i++) {
            Camera.getCameraInfo(i, cameraInfo);
            if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
                defaultCameraId = i;
            }
        }
}

    protected void onResume() {

        super.onResume();

        Log.i(TAG, " ");
        Log.i(TAG, "|||||||||||||||||||   ON RESUME  |||||||||||||||||||");
        Log.i(TAG, " ");

        getWindow().setFormat(PixelFormat.TRANSLUCENT);

        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        camera = Camera.open(defaultCameraId);
        preview = new StackOPreview(this, camera);
        setContentView(preview);

        // *** new control layer that contains the TakePicture button ***
        controlInflater = LayoutInflater.from(getBaseContext());

        viewControl = controlInflater.inflate(R.layout.control_preview, null);

        LayoutParams layoutParamsControl = new LayoutParams(
                LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);

        this.addContentView(viewControl, layoutParamsControl);

        photoSaver = new PhotoSaver(this);

        buttonTakePicture = (ImageButton) findViewById(R.id.takepicture);

        buttonTakePicture.setOnClickListener(new Button.OnClickListener() {

            @Override
            public void onClick(View v) {
                Log.v(TAG,
                        "in Take Picture button on click, launching photSaver");
                camera.takePicture(null, null, photoSaver);
            }
        });

        // table button
        Button tableButton = (Button) findViewById(R.id.display_table);

        TableButtonListener tableButtonListener = new TableButtonListener(this);
        tableButton.setOnClickListener(tableButtonListener);
    }
于 2013-08-15T10:14:15.027 回答