Join FlipAndroid.COM Telegram Group: https://t.me/joinchat/F_aqThGkhwcLzmI49vKAiw


¿Cómo se relacionan las devoluciones de llamada de SurfaceHolder con el ciclo de vida de la actividad?

He estado intentando implementar una aplicación que requiere una vista previa de la cámara en una superficie. Como veo las cosas, los ciclos de vida de la actividad y de la superficie consisten en los siguientes estados:

  1. Cuando inicio mi Actividad: onResume()->onSurfaceCreated()->onSurfaceChanged()
  2. Cuando dejo mi Actividad: onPause()->onSurfaceDestroyed()

En este esquema, puedo hacer llamadas correspondientes como abrir / liberar cámara y iniciar / detener vista previa en onPause/onResume y onSurfaceCreated()/onSurfaceDestroyed() .

Funciona bien, a menos que bloquee la pantalla. Cuando lanzo la aplicación, luego bloqueo la pantalla y desbloquearla más tarde veo:

onPause() – y nada más después de bloquear la pantalla – luego onResume() después de desbloquear – y no hay devoluciones de superficie después de ese momento. En realidad, onResume() se llama después de pulsar el botón de encendido y la pantalla está encendida, pero la pantalla de bloqueo sigue activa, por lo que es antes de que la actividad se vuelva visible.

Con este esquema, obtengo una pantalla en negro después del desbloqueo, y no se llaman devoluciones de superficie.

Aquí hay un fragmento de código que no implica trabajo real con la cámara, pero las SurfaceHolder llamada SurfaceHolder . El problema anterior se reproduce incluso con este código en mi teléfono (las devoluciones de llamada se llaman en una secuencia normal cuando presiona el botón "Atrás", pero faltan al bloquear la pantalla):

 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"); } } 

¿Alguna idea de por qué la superficie permanece sin destruir después de que la actividad se detiene? Además, ¿cómo maneja el ciclo de vida de la cámara en estos casos?

  • ERROR: createWindowSurface falló EGL_BAD_ALLOC
  • Establecer el color de fondo de la vista de superficie
  • El EditText de Android se oculta cuando se muestra el teclado virtual y está involucrado SurfaceView
  • Antialiasing en TextureView
  • ¿Cómo puedo establecer el tamaño de vista previa de la cámara a la relación de aspecto cuadrado en un SurfaceView cuadrado (como Instagram)
  • Vista superficial + glsurfaceview + framelayout
  • ¿Qué diferencias hay entre Surfaceview y TextureView?
  • Android Advertencia: CHECK surface infomation creating = false
  • 5 Solutions collect form web for “¿Cómo se relacionan las devoluciones de llamada de SurfaceHolder con el ciclo de vida de la actividad?”

    Editar: si el targetSDK es mayor que 10, poner la aplicación en suspensión llama a onPause y onStop . Fuente

    Miré el ciclo de vida de la actividad y el SurfaceView en una pequeña aplicación de cámara en mi teléfono de pan de jengibre. Tienes toda la razón; La superficie no se destruye cuando se presiona el botón de encendido para poner el teléfono en reposo. Cuando el teléfono se va a dormir, la Actividad se onPause . (Y no hace onStop .) Hace onResume cuando el teléfono se despierta, y, como usted señala, lo hace mientras la pantalla de bloqueo sigue siendo visible y aceptar la entrada, que es un poco extraño. Cuando hago que la actividad sea invisible presionando el botón de inicio, la actividad hace tanto onPause como onStop . Algo provoca una devolución de llamada a surfaceDestroyed en este caso entre el final de onPause y el inicio de onStop . No es muy obvio, pero parece muy consistente.

    Cuando se pulsa el botón de encendido para poner en suspensión el teléfono, a menos que se haga algo explícitamente para detenerlo, ¡la cámara sigue funcionando! Si tengo la cámara hacer una devolución de llamada por imagen para cada marco de vista previa, con un Log.d () allí, las declaraciones de registro siguen llegando mientras el teléfono está fingiendo dormir. Creo que es muy Sneaky .

    Como otra confusión, las devoluciones de llamada a surfaceCreated y surfaceChanged suceden después onResume en la actividad, si se está creando la superficie.

    Como regla general, manejo la cámara en la clase que implementa las devoluciones de llamada 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; } } } 

    Y luego en la Actividad:

     @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(); } 

    Y que parece funcionar bien para mí. Los eventos de rotación causan que la Actividad sea destruida y recreada, lo que hace que SurfaceView sea destruido y recreado también.

    Otra solución simple que funciona bien – para cambiar la visibilidad de la superficie de vista previa.

     private SurfaceView preview; 

    La vista previa es init en el método onCreate . En el método onResume establezca View.VISIBLE para la superficie de vista previa:

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

    Y respectivamente en la visibilidad establecida View.GONE :

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

    SurfaceHolder.Callback está relacionado con su superficie.

    ¿Está la actividad en la pantalla? Si es así, no habrá SurfaceHolder.Callback, porque la Superficie sigue en la pantalla.

    Para controlar cualquier SurfaceView, solo puede manejarlo en onPause / onResume. Para SurfaceHolder.Callback, puedes usarlo si la Superficie es cambiada (creada, sizechanged, y destruida), como inicializar openGL cuando surfaceCreated, y destruir openGL cuando surfaceDestroyed, etc.

    Gracias a las dos respuestas anteriores, me las arreglé para hacer mi vista previa de la cámara de trabajo claramente al volver de fondo o lockscreen.

    Como @ e7fendy mencionó, la devolución de llamada del SurfaceView no se llamará mientras está en screenlock, ya que la vista de la superficie sigue siendo visible para el sistema.

    Por lo tanto, como @ valididcat aconsejó, llamando preview.setVisibility(View.VISIBLE); Y preview.setVisibility(View.GONE); En respectivamente onPause () y onResume () forzará la vista de superficie a relayout y lo llamará callbacks.

    Para entonces, la solución de @ emrys57 más estas dos llamadas del método de la visibilidad arriba hará su trabajo de la inspección previo de la cámara claramente 🙂

    Así que sólo puedo dar +1 a cada uno de ustedes como todos ustedes lo merecían;)

    Esta es una solución alternativa para todos los métodos de devolución de llamada, que pueden estar sujetos al mismo comportamiento de orden de eventos no definido con ciclo de actividad. A menos que vayas a inspeccionar todo el código de Android para cada llamada de regreso que utiliza para determinar el desencadenador de origen y que el control de las implementaciones y esperamos que la base de código no ha cambiado en el futuro, se puede realmente decir que la orden de eventos entre callsbacks Y los eventos del ciclo de vida de la actividad podrían ser garantizados.

    En la actualidad, estas interacciones de orden pueden referirse típicamente a un comportamiento indefinido, con fines de desarrollo.

    Así que lo mejor sería siempre manejar correctamente este comportamiento indefinido, de tal manera que nunca será un problema en primer lugar, por asegurar que los pedidos son comportamiento definido.

    Mi Sony Xperia, por ejemplo, en el sueño, ciclos mi aplicación actual, destruyendo la aplicación y luego reiniciarlo y lo pone en el estado de pausa, lo creas o no.

    ¿Cuánto prueba de conducta de eventos de prueba google proporciona en su SDK como prueba especial construir para el entorno de acogida implementa no sé, pero sin duda tienen que hacer un esfuerzo para garantizar, los comportamientos de las órdenes de eventos son todos bloqueados por ser bastante estricta en la importar.

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

    Import android.util.Log; Import android.util.SparseArray;

    / ** * Creado por woliver el 2016/06/24. * * Entorno de host Android, dicta un Ciclo de Vida de Actividad para OnCreate, onStart, onResume, onPause, onStop, onDestory, * donde estamos necesitamos liberar memoria y manejar para que otras aplicaciones usen. * Cuando se reanuda se nos requiere a veces para volver a vincular y activar estos elementos con otros objetos. * Normalmente, estos otros objetos proporcionan métodos de devolución de llamada desde el entorno de host que proporcionan un onCreated y onDestroy, en el que sólo podemos enlazar a este objeto de OnCreated y loose * out bind onDestory. * Estos tipos de métodos de devolución de llamada, el tiempo de ejecución es controlado por nuestro entorno anfitrión * y no son garantías de que el comportamiento / orden de ejecución del Ciclo de Vida de la Actividad y de estos métodos de llamada * permanezca constante. * A efectos de desarrollo, las interacciones y el orden de ejecución pueden llamarse técnicamente indefinidos *, ya que corresponde al implementador de implementación host, samsung, sony, htc. * * Vea el siguiente documento del desarrollador: https://developer.android.com/reference/android/app/Activity.html * Quote: * Si una actividad está completamente oscurecida por otra actividad, se detiene. Todavía conserva toda la información de estado * y miembro, sin embargo, ya no es visible para el usuario por lo que su ventana está * oculta y que a menudo será asesinado por el sistema cuando la memoria se necesita en otro lugar. * EndQuato: * * Si la actividad no está oculta, entonces cualquier devolución de llamada que uno hubiera esperado que haya sido llamada por el sistema host *, no habrá sido llamada, como OnCreate y OnDestory métodos de interfaz de devolución de llamada SurfaceView. * Esto significa que tendrá que detener el objeto que se ha vinculado a SurfaceView como una cámara * en pausa y nunca volverá a vincular el objeto como la devolución de llamada OnCreate nunca se llamará. * * /

     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(); } 

    Ejemplo de Implementación y uso de clase, que trata o el comportamiento indefinido de los implementadores de entorno anfitrión 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); } } 
    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.