SurfaceHoldercallback如何与Activity生命周期相关?

我一直在试图实现一个需要在表面上预览相机的应用程序。 正如我所看到的,活动和表面生命周期都包含以下几个状态:

  1. 当我第一次启动我的Activity: onResume()->onSurfaceCreated()->onSurfaceChanged()
  2. 当我离开我的Activity: onPause()->onSurfaceDestroyed()

在这个scheme中,我可以在onPause/onResumeonSurfaceCreated()/onSurfaceDestroyed()做相应的调用,如打开/释放摄像头和启动/停止预览。

它工作正常,除非我locking屏幕。 当我启动应用程序,然后locking屏幕,稍后解锁,我看到:

onPause() – 在屏幕locking后没有其他任何事情 – 然后解锁后的onResume()之后没有表面callback。 实际上,在按下电源button并且屏幕打开之后调用onResume() ,但locking屏幕仍然处于活动状态,所以在活动变得可见之前。

通过这个scheme,解锁后我得到一个黑屏,并且不会调用表面callback。

这是一个代码片段,不涉及实际的相机工作,但SurfaceHoldercallback。 即使在手机上有这个代码,上面的问题也会被重现(当您按下“返回”button时,以正常顺序调用callback,但当您locking屏幕时,callback会丢失):

 class Preview extends SurfaceView implements SurfaceHolder.Callback { private static final String tag= "Preview"; public Preview(Context context) { super(context); Log.d(tag, "Preview()"); SurfaceHolder holder = getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { Log.d(tag, "surfaceCreated"); } public void surfaceDestroyed(SurfaceHolder holder) { Log.d(tag, "surfaceDestroyed"); } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { Log.d(tag, "surfaceChanged"); } } 

活动暂停后为什么表面仍然未被破坏的任何想法? 另外,在这种情况下,你如何处理相机的生命周期?

编辑:如果targetSDK大于10,应用程序进入睡眠状态调用onPause onStop 。 资源

我在姜饼手机上的一个小照相机应用程序中查看了Activity和SurfaceView的生命周期。 你是完全正确的; 当按下电源button使手机进入睡眠状态时,表面不会被破坏。 当手机进入睡眠状态时,Activity会暂停。 (而不是onStop 。)当手机醒来的时候它会执行onResume ,正如你指出的那样,当锁屏仍然可见并且接受input时,它会这样做,这有点奇怪。 当我通过按主页button使活动不可见时,Activity将在onPauseonStop 。 在这种情况下,在onPause结束和onStop开始之间,会引起一个callback到surfaceDestroyed 。 这不是很明显,但似乎非常一致。

当按下电源button使手机进入睡眠状态时,除非有明确的要求停止手机,否则相机会继续运行! 如果我有摄像机为每个预览帧执行每个图像的callback,那里有一个Log.d(),当电话假装睡觉时,日志语句不断出现。 我认为这是非常狡猾的

另一个困惑是,如果表面正在被创build,则表面创build和surfaceChanged的callback会 onResume 之后发生。

一般来说,我在实现SurfaceHoldercallback的类中pipe理相机。

 class Preview extends SurfaceView implements SurfaceHolder.Callback { private boolean previewIsRunning; private Camera camera; public void surfaceCreated(SurfaceHolder holder) { camera = Camera.open(); // ... // but do not start the preview here! } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { // set preview size etc here ... then myStartPreview(); } public void surfaceDestroyed(SurfaceHolder holder) { myStopPreview(); camera.release(); camera = null; } // safe call to start the preview // if this is called in onResume, the surface might not have been created yet // so check that the camera has been set up too. public void myStartPreview() { if (!previewIsRunning && (camera != null)) { camera.startPreview(); previewIsRunning = true; } } // same for stopping the preview public void myStopPreview() { if (previewIsRunning && (camera != null)) { camera.stopPreview(); previewIsRunning = false; } } } 

然后在活动中:

 @Override public void onResume() { preview.myStartPreview(); // restart preview after awake from phone sleeping super.onResume(); } @Override public void onPause() { preview.myStopPreview(); // stop preview in case phone is going to sleep super.onPause(); } 

这对我来说似乎是行得通的。 旋转事件导致活动被破坏和重新创build,导致SurfaceView被破坏和重新创build。

另一个简单的解决scheme,工作正常 – 改变预览表面的可见性。

 private SurfaceView preview; 

onCreate方法中预览是init。 在onResume方法中设置View.VISIBLE预览面:

 @Override public void onResume() { preview.setVisibility(View.VISIBLE); super.onResume(); } 

并分别在onPause设置可见性View.GONE

 @Override public void onPause() { super.onPause(); preview.setVisibility(View.GONE); stopPreviewAndFreeCamera(); //stop and release camera } 

SurfaceHolder.Callback和Surface有关。

在屏幕上的活动? 如果是这样,将不会有SurfaceHolder.Callback,因为Surface仍然在屏幕上。

要控制任何SurfaceView,只能在onPause / onResume中处理它。 对于SurfaceHolder.Callback,如果Surface被更改(创build,大小更改和销毁),则可以使用它,例如在surfaceCreated时初始化openGL,以及在surfaceDestroyed时销毁openGL等。

感谢所有以前的答案,我设法让我的相机预览工作从背景或锁屏返回。

正如@ e7fendy所说,SurfaceView的callback在屏幕locking时不会被调用,因为表面视图对于系统仍然是可见的。

因此,如@validcatbuild议,调用preview.setVisibility(View.VISIBLE);preview.setVisibility(View.GONE); 分别onPause()和onResume()将强制表面视图重新布局本身,并将其称为callback。

那么,从@ emrys57的解决scheme加上这两个可见性方法调用上面将使您的相机预览工作明白:)

所以我只能给你们每个人+1,因为你们都应得的;)

这是所有callback方法的替代解决scheme,可能全部受到与活动周期相同的未定义事件顺序行为的影响。 除非要检查每个callback的所有android代码,否则可以使用它来确定原始触发器和控制实现的人员,并希望将来代码库不会发生更改,是否可以真正指出callback函数之间的事件顺序活动生命周期事件可以得到保证。

目前,这些订单的相互作用通常可以被称为未定义的行为,用于开发目的。

所以最好的办法是始终正确地处理这个未定义的行为,这样它就永远不会成为一个问题,通过确保订单是定义的行为。

例如,我的索尼Xperia睡眠,循环我当前的应用程序,通过销毁应用程序,然后重新启动它,并把它置于暂停状态,信不信由你。

多less事件顺序行为testing谷歌提供的SDK作为特殊的testing版本的主机环境实现我不知道,但他们肯定需要努力确保,事件订单的行为都被locking在相当严格物。

https://code.google.com/p/android/issues/detail?id=214171&sort=-opened&colspec=ID%20Status%20Priority%20Owner%20Summary%20Stars%20Reporter%20Opened

导入android.util.Log; 导入android.util.SparseArray;

/ ** *在2016/06/24由woliver创build。 * * Android主机环境,为OnCreate,onStart,onResume,onPause,onStop,onDestory *指定一个Activity生命周期,我们需要释放内存并处理其他应用程序使用。 *简历时,我们有时需要重新绑定和激活这些项目与其他对象。 *通常,这些其他对象提供来自主机环境的callback方法,这些方法提供了一个onCreated和onDestroy,我们只能从OnCreated绑定到这个对象,并且绑定onDestory。 *这些types的callback方法,shedual运行时间是由我们的主机环境*控制的,并且它们不保证活动生命周期的执行行为/顺序和这些callback方法*保持一致。 *为了开发的目的,交互和执行的顺序在技术上可以称为undefined *,因为它取决于主机实现实施者,samsung,sony,htc。 * *请参阅以下开发人员文档: https : //developer.android.com/reference/android/app/Activity.html * Quote:*如果一项活动被其他活动完全遮蔽,则会被停止。 它仍然保留所有的状态和成员信息,但是用户不再可见,所以窗口被隐藏起来,而且在其他地方需要内存的时候,通常会被系统杀死。 * EndQuato:* *如果activity没有被隐藏,那么任何被主机*系统调用的callback都不会被调用,比如OnCreate和OnDestory方法接口SurfaceView的callback。 *这意味着你将不得不停止绑定到SurfaceView的对象(例如摄像机*),并且永远不会重新绑定对象,因为OnCreatecallback将永远不会被调用。 * * /

 public abstract class WaitAllActiveExecuter<Size> { private SparseArray<Boolean> mReferancesState = null; // Use a dictionary and not just a counter, as hosted code // environment implementer may make a mistake and then may double executes things. private int mAllActiveCount = 0; private String mContextStr; public WaitAllActiveExecuter(String contextStr, int... identifiers) { mReferancesState = new SparseArray<Boolean>(identifiers.length); mContextStr = contextStr; for (int i = 0; i < identifiers.length; i++) mReferancesState.put(identifiers[i], false); } public void ActiveState(int identifier) { Boolean state = mReferancesState.get(identifier); if (state == null) { // Typically panic here referance was not registered here. throw new IllegalStateException(mContextStr + "ActiveState: Identifier not found '" + identifier + "'"); } else if(state == false){ mReferancesState.put(identifier, true); mAllActiveCount++; if (mAllActiveCount == mReferancesState.size()) RunActive(); } else { Log.e(mContextStr, "ActivateState: called to many times for identifier '" + identifier + "'"); // Typically panic here and output a log message. } } public void DeactiveState(int identifier) { Boolean state = mReferancesState.get(identifier); if (state == null) { // Typically panic here referance was not registered here. throw new IllegalStateException(mContextStr + "DeActiveState: Identifier not found '" + identifier + "'"); } else if(state == true){ if (mAllActiveCount == mReferancesState.size()) RunDeActive(); mReferancesState.put(identifier, false); mAllActiveCount--; } else { Log.e(mContextStr,"DeActiveState: State called to many times for identifier'" + identifier + "'"); // Typically panic here and output a log message. } } private void RunActive() { Log.v(mContextStr, "Executing Activate"); ExecuterActive(); } private void RunDeActive() { Log.v(mContextStr, "Executing DeActivate"); ExecuterDeActive(); } abstract public void ExecuterActive(); abstract public void ExecuterDeActive(); } 

类的实现和使用的例子,处理或者未定义的android主机环境实现者的行为。

 private final int mBCTSV_SurfaceViewIdentifier = 1; private final int mBCTSV_CameraIdentifier = 2; private WaitAllActiveExecuter mBindCameraToSurfaceView = new WaitAllActiveExecuter("BindCameraToSurfaceViewe", new int[]{mBCTSV_SurfaceViewIdentifier, mBCTSV_CameraIdentifier}) { @Override public void ExecuterActive() { // Open a handle to the camera, if not open yet and the SurfaceView is already intialized. if (mCamera == null) { mCamera = Camera.open(mCameraIDUsed); if (mCamera == null) throw new RuntimeException("Camera could not open"); // Look at reducing the calls in the following two methods, some this is unessary. setDefaultCameraParameters(mCamera); setPreviewSizesForCameraFromSurfaceHolder(getSurfaceHolderForCameraPreview()); } // Bind the Camera to the SurfaceView. try { mCamera.startPreview(); mCamera.setPreviewDisplay(getSurfaceHolderForCameraPreview()); } catch (IOException e) { e.printStackTrace(); ExecuterDeActive(); throw new RuntimeException("Camera preview could not be set"); } } @Override public void ExecuterDeActive() { if ( mCamera != null ) { mCamera.stopPreview(); mCamera.release(); mCamera = null; } } }; @Override protected void onPause() { mBindCameraToSurfaceView.DeactiveState(mBCTSV_CameraIdentifier); Log.v(LOG_TAG, "Activity Paused - After Super"); } @Override public void onResume() { mBindCameraToSurfaceView.ActiveState(mBCTSV_CameraIdentifier); } private class SurfaceHolderCallback implements SurfaceHolder.Callback { public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { Log.v(LOG_TAG, "Surface Changed"); } public void surfaceCreated(SurfaceHolder surfaceHolder) { Log.v(LOG_TAG, "Surface Created"); mBindCameraToSurfaceView.ActiveState(mBCTSV_SurfaceViewIdentifier); } public void surfaceDestroyed(SurfaceHolder arg0) { Log.v(LOG_TAG, "Surface Destoryed"); mBindCameraToSurfaceView.DeactiveState(mBCTSV_SurfaceViewIdentifier); } }