我的主要活动展示了相机预览。当我旋转手机时,应用程序关闭。在日志中,我看到在旋转后从不调用 surfaceCreated() 和 surfaceChanged(),然后调用 main 的 onStop()。
摩托罗拉和三星手机表现出相同的行为。我的应用程序中的其他活动按预期在轮换中存活。
我认为问题可能是过时的预览参考,但日志显示新的持有者和预览实例。
- 为什么轮换后不调用持有者回调?
- 我可以强迫他们打电话吗?
日志片段
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;
}
}