Android SurfaceHolder 回调如何与 Activity 生命周期相关?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11495842/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How SurfaceHolder callbacks are related to Activity lifecycle?
提问by krugloid
I've been trying to implement an application that requires camera preview on a surface. As I see the things, both activity and surface lifecycles consist of the following states:
我一直在尝试实现一个需要在表面上进行相机预览的应用程序。在我看来,活动和表面生命周期都包含以下状态:
- When I first launch my Activity:
onResume()->onSurfaceCreated()->onSurfaceChanged()
- When I leave my Activity:
onPause()->onSurfaceDestroyed()
- 当我第一次启动我的活动时:
onResume()->onSurfaceCreated()->onSurfaceChanged()
- 当我离开我的活动时:
onPause()->onSurfaceDestroyed()
In this scheme, I can do corresponding calls like open/release camera and start/stop preview in onPause/onResume
and onSurfaceCreated()/onSurfaceDestroyed()
.
在此方案中,我可以做相应的像在开/释放摄像头和启动/停止预览电话onPause/onResume
和onSurfaceCreated()/onSurfaceDestroyed()
。
It works fine, unless I lock the screen. When I launch the app, then lock the screen and unlock it later I see:
它工作正常,除非我锁定屏幕。当我启动应用程序,然后锁定屏幕并稍后解锁时,我看到:
onPause()
- and nothing else after the screen is locked - then onResume()
after unlock - and no surface callbacks after then. Actually, onResume()
is called after the power button is pressed and the screen is on, but the lock screen is still active, so, it's before the activity becomes even visible.
onPause()
- 屏幕锁定后没有别的 - 然后是onResume()
解锁后 - 之后没有表面回调。实际上,onResume()
在按下电源按钮并打开屏幕后调用,但锁定屏幕仍然处于活动状态,因此,它是在活动变得可见之前。
With this scheme, I get a black screen after unlock, and no surface callbacks are called.
使用这个方案,我在解锁后得到一个黑屏,并且没有调用任何表面回调。
Here's a code fragment that doesn't involve actual work with the camera, but the SurfaceHolder
callbacks. The issue above is reproduced even with this code on my phone (callbacks are called in a normal sequence when you press "Back" button, but are missing when you lock the screen):
这是一个代码片段,不涉及相机的实际工作,但涉及SurfaceHolder
回调。即使在我的手机上使用此代码也会重现上述问题(按“返回”按钮时按正常顺序调用回调,但在锁定屏幕时会丢失):
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");
}
}
Any ideas on why the surface remains undestroyed after the Activity is paused? Also, how do you handle camera lifecycle in such cases?
关于为什么在 Activity 暂停后表面仍未被破坏的任何想法?另外,在这种情况下,您如何处理相机生命周期?
采纳答案by emrys57
Edit:if the targetSDK is greater than 10, putting the app to sleep calls onPause
andonStop
. Source
编辑:如果 targetSDK 大于 10,则将应用置于睡眠调用onPause
和onStop
. 来源
I looked at the lifecycle of both the Activity and the SurfaceView in a tiny camera app on my gingerbread phone. You are entirely correct; the surface is not destroyed when the power button is pressed to put the phone to sleep. When the phone goes to sleep, the Activity does onPause
. (And does not do onStop
.) It does onResume
when the phone wakes up, and, as you point out, it does this while the lock screen is still visible and accepting input, which is a bit odd. When I make the Activity invisible by pressing the Home button, the Activity does both onPause
and onStop
. Something causes a callback to surfaceDestroyed
in this case between the end of onPause
and the start of onStop
. It's not very obvious, but it does seem very consistent.
我在姜饼手机上的一个微型相机应用程序中查看了 Activity 和 SurfaceView 的生命周期。你是完全正确的;当按下电源按钮使手机进入睡眠状态时,表面不会被破坏。当手机进入睡眠状态时,活动会执行onPause
。(并且不这样做onStop
。)onResume
当手机唤醒时它会这样做,并且正如您所指出的那样,它会在锁定屏幕仍然可见并接受输入时执行此操作,这有点奇怪。当我通过按主页按钮使 Activity 不可见时,该 Activity 会同时执行onPause
和onStop
. surfaceDestroyed
在这种情况下,某些事情会导致回调到结束onPause
和开始之间onStop
。这不是很明显,但看起来确实很一致。
When the power button is pressed to sleep the phone, unless something is explicitly done to stop it, the camera keeps running! If I have the camera do a per-image callback for each preview frame, with a Log.d() in there, the log statements keep coming while the phone is pretending to sleep. I think that is Very Sneaky.
当按下电源按钮使手机休眠时,除非明确采取措施将其停止,否则相机会继续运行!如果我让相机为每个预览帧执行每个图像的回调,其中有一个 Log.d(),那么当手机假装睡觉时,日志语句会不断出现。我认为这是非常狡猾的。
As another confusion, the callbacks to surfaceCreated
and surfaceChanged
happen afteronResume
in the activity, if the surface is being created.
另一个混淆是,如果正在创建表面,则回调surfaceCreated
和在活动之后surfaceChanged
发生。onResume
As a rule, I manage the camera in the class that implements the SurfaceHolder callbacks.
通常,我在实现 SurfaceHolder 回调的类中管理相机。
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;
}
}
}
and then in the Activity:
然后在活动中:
@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();
}
and that seems to work OK for me. Rotation events cause the Activity to be destroyed and recreated, which causes the SurfaceView to be destroyed and recreated too.
这对我来说似乎没问题。旋转事件会导致 Activity 被销毁和重新创建,这也会导致 SurfaceView 被销毁和重新创建。
回答by validcat
Another simple solution that works fine - to change visibility of the preview surface.
另一个工作正常的简单解决方案 - 更改预览表面的可见性。
private SurfaceView preview;
preview is init in onCreate
method. In onResume
method set View.VISIBLE
for preview surface:
预览在onCreate
方法中初始化。在为预览表面onResume
设置View.VISIBLE
的方法中:
@Override
public void onResume() {
preview.setVisibility(View.VISIBLE);
super.onResume();
}
and respectively in onPause
set visibility View.GONE
:
并分别在onPause
设置可见性中View.GONE
:
@Override
public void onPause() {
super.onPause();
preview.setVisibility(View.GONE);
stopPreviewAndFreeCamera(); //stop and release camera
}
回答by ptitvinou
Thanks to both all previous answers I managed to make my camera preview work plainly while going back from either background or lockscreen.
感谢之前所有的答案,我设法让我的相机预览在从背景或锁屏返回时清晰地工作。
As @e7fendy mentionned, the SurfaceView's callback won't be called while on screenlock as the surface view is still visible for the system.
正如@e7fendy 所提到的,在屏幕锁定时不会调用 SurfaceView 的回调,因为表面视图对系统仍然可见。
Hence, as @validcat advised, calling preview.setVisibility(View.VISIBLE);
and preview.setVisibility(View.GONE);
in respectively onPause() and onResume() will force the surface view to relayout itself and will call it callbacks.
因此,正如@validcat 所建议的,分别在 onPause() 和 onResume() 中调用preview.setVisibility(View.VISIBLE);
和preview.setVisibility(View.GONE);
将强制表面视图重新布局自身并将其称为回调。
By then, the solution from @emrys57 plus these two visibility method calls above will make your camera preview work plainly :)
届时,@emrys57 的解决方案加上上面这两个可见性方法调用将使您的相机预览工作起来很简单:)
So I can only give +1 to each of you as you all deserved it ;)
所以我只能给你们每个人+1,因为你们都应得的;)
回答by e7fendy
SurfaceHolder.Callback is related to its Surface.
SurfaceHolder.Callback 与其 Surface 相关。
Is the activity on the screen? If so, there won't be SurfaceHolder.Callback, because the Surface is still on the screen.
屏幕上有活动吗?如果是这样,就不会有 SurfaceHolder.Callback,因为 Surface 还在屏幕上。
To control any SurfaceView, you can handle it in onPause/onResume only. For SurfaceHolder.Callback, you can use it if the Surface is changed (created, sizechanged, and destroyed), like initialize openGL when surfaceCreated, and destroy openGL when surfaceDestroyed, etc.
要控制任何 SurfaceView,您只能在 onPause/onResume 中处理它。对于SurfaceHolder.Callback,可以在Surface发生变化(创建、大小改变、销毁)时使用它,比如surfaceCreated时初始化openGL,surfaceDestroyed时销毁openGL等。
回答by Wesley Oliver
Here is an alternative solution for all callback methods, that may all be subject to the same undefined event order behavior with activity cycle. Unless you going to inspect all the android code for each call back you use to determine the origin trigger and who controlling the implementations and hope that the code base doesn't changed in the future, can one really state, that the event order between callsbacks and activity life cycle events could be guaranteed.
这是所有回调方法的替代解决方案,所有回调方法都可能受到具有活动周期的相同未定义事件顺序行为的影响。除非您要检查用于确定源触发器和控制实现的每个回调的所有 android 代码,并希望将来代码库不会更改,否则可以真正声明回调之间的事件顺序并且可以保证活动生命周期事件。
Right now these interactions of order can typically be referred to as undefined behavior, for development purposes.
现在,出于开发目的,这些顺序交互通常可以称为未定义行为。
So best would be to always correctly handle this undefined behavior, such that it will never be a problem in the first place, by ensure the orders are defined behavior.
所以最好总是正确处理这种未定义的行为,这样它就永远不会成为问题,确保订单是定义的行为。
My Sony Xperia for instance, on sleep, cycles my current app, by destroying the app and then restarting it and puts it into the pause state, believe it or not.
例如,我的 Sony Xperia 在睡眠时循环我当前的应用程序,通过销毁应用程序然后重新启动它并将其置于暂停状态,信不信由你。
How much event ordering behavior testing google provides in their SDK as special test build for host environment implements I don't know, but they definitely need to make an effort to ensure, behaviors of event orders are all locked down by being rather strict on the matter.
谷歌在他们的 SDK 中提供了多少事件排序行为测试作为宿主环境工具的特殊测试版本我不知道,但他们肯定需要努力确保,事件命令的行为都通过对事情。
import android.util.Log; import android.util.SparseArray;
导入 android.util.Log; 导入 android.util.SparseArray;
/** * Created by woliver on 2016/06/24. * * Android host environment, dictates an Activity Life Cycle for OnCreate, onStart, onResume, onPause, onStop, onDestory, * where by we are require to release memory and handles for other applications to use. * When resume we are required at times to rebind and activate these items with other objects. * Typically these other objects provide callback methods from the host enviroment which provide * an onCreated and onDestroy, in which we can only bind to this object from OnCreated and and loose * out bind onDestory. * These types of call back methods, shedual time to run is controller by our host enviroment * and their are no guarantees to that the behaviour/order of execution of the Activity Life Cycle and these call back methods * remains consistent. * For the purpose of development the interactions and order of execution can technically be called undefined * as it is up to the host implementation implementer, samsung, sony, htc. * * See following developer document: https://developer.android.com/reference/android/app/Activity.html* Quote: * If an activity is completely obscured by another activity, it is stopped. It still retains all state * and member information, however, it is no longer visible to the user so its window is * hidden and it will often be killed by the system when memory is needed elsewhere. * EndQuato: * * If the activity is not hidden, then any callbacks that one would have expected to have been call by the host * system, will not have been called, such as OnCreate and OnDestory methods interface SurfaceView callback. * This means that you will have to stop the object that has been binded to SurfaceView such as a camera * in pause and will never rebind the object as the OnCreate callback will never be called. * */
/** * 由 woliver 于 2016/06/24 创建。* * Android 主机环境,规定了 OnCreate、onStart、onResume、onPause、onStop、onDestory 的 Activity 生命周期,* 我们需要释放内存和句柄供其他应用程序使用。* 恢复时,我们有时需要将这些项目与其他对象重新绑定并激活。* 通常,这些其他对象提供来自宿主环境的回调方法,这些方法提供了 *onCreated 和 onDestroy,其中我们只能从 OnCreated 绑定到此对象,并且松散 * out 绑定 onDestory。* 这些类型的回调方法,运行时间由我们的主机环境控制 * 并且它们不能保证活动生命周期的行为/执行顺序和这些回调方法 * 保持一致。* 出于开发目的,交互和执行顺序在技术上可以称为未定义 * 因为这取决于主机实现实施者,三星,索尼,HTC。* * 请参阅以下开发人员文档:https://developer.android.com/reference/android/app/Activity.html* 引用: * 如果一个 Activity 被另一个 Activity 完全遮挡,它就会停止。它仍然保留所有状态 * 和成员信息,但是,它不再对用户可见,因此它的窗口 * 隐藏,并且当其他地方需要内存时,它通常会被系统杀死。* EndQuato: * * 如果活动没有隐藏,那么任何预期被宿主系统调用的回调都不会被调用,例如 OnCreate 和 OnDestory 方法接口 SurfaceView 回调。* 这意味着您必须暂停已绑定到 SurfaceView 的对象(例如相机) * 并且永远不会重新绑定该对象,因为永远不会调用 OnCreate 回调。* */
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();
}
Example of Implementation and use of class, which deals with or the undefined behaviour of android host enviroment implementers.
类的实现和使用示例,用于处理 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);
}
}