72 votes

Comment les rappels SurfaceHolder sont-ils liés au cycle de vie de l'activité?

J'ai essayé de mettre en œuvre une application qui nécessite une prévisualisation de la caméra sur une surface. Comme je vois les choses, à la fois de l'activité et de la surface des cycles de vie se composent des états suivants:

  1. Quand j'ai lancer mon Activité: onResume()->onSurfaceCreated()->onSurfaceChanged()
  2. Quand je laisse mon Activité: onPause()->onSurfaceDestroyed()

Dans ce schéma, je peux faire des appels correspondants comme open/déblocage de l'appareil photo et commencer/arrêter la prévisualisation en onPause/onResume et onSurfaceCreated()/onSurfaceDestroyed().

Il fonctionne très bien, sauf que je verrouiller l'écran. Lorsque je lance l'application, puis verrouiller l'écran et débloquer plus tard, je vois:

onPause() - et rien d'autre après l'écran est verrouillé - ensuite, onResume() après déverrouillage et pas en surface rappels ensuite. En fait, onResume() est appelée après que le bouton d'alimentation est pressé et que l'écran est allumé, mais l'écran de verrouillage est toujours actif, donc avant l'activité devient encore visible.

Avec ce régime, j'obtiens un écran noir après le déblocage, et pas en surface rappels sont appelés.

Voici un fragment de code qui n'implique pas de réel travail avec la caméra, mais l' SurfaceHolder rappels. La question ci-dessus est reproduit, même avec ce code sur mon téléphone (rappels sont appelés dans une séquence normale lorsque vous appuyez sur le bouton "Arrière", mais sont manquantes lorsque vous verrouillez l'écran):

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

Toutes les idées sur le pourquoi de la surface reste undestroyed après l'Activité est en pause? Aussi, comment gérez-vous la caméra du cycle de vie dans de tels cas?

58voto

emrys57 Points 2300

J'ai regardé le cycle de vie de la fois de l'Activité et de la SurfaceView dans une minuscule caméra app sur mon pain d'épice de téléphone. Vous êtes tout à fait correcte; la surface n'est pas détruite quand le bouton d'alimentation est enfoncé pour mettre le téléphone en veille. Lorsque le téléphone passe en mode veille, l'Activité n' onPause. (Et ne fait pas d' onStop.) Il n' onResume lorsque le téléphone se réveille, et, comme vous le soulignez, il le fait tout l'écran de verrouillage est toujours visible et accepter l'entrée, ce qui est un peu bizarre. Quand je fais de l'Activité invisible en appuyant sur le bouton Home, l'Activité n'est à la fois onPause et onStop. Quelque chose entraîne un rappel à l' surfaceDestroyed dans ce cas entre la fin de l' onPause et le début de l' onStop. Il n'est pas très évident, mais il ne semble pas très cohérent.

Lorsque le bouton d'alimentation est pressé de dormir le téléphone, sauf si quelque chose est explicitement fait pour l'arrêter, l'appareil continue de fonctionner! Si j'ai l'appareil photo faire image par image, la fonction de rappel pour chaque aperçu d'image, avec un Journal.d (), le journal des déclarations de garder à venir alors que le téléphone est faire semblant de dormir. Je pense que c'est Très Sournois.

Comme une autre confusion, les rappels à l' surfaceCreated et surfaceChanged se produire après l' onResume de l'activité, si la surface est en cours de création.

En règle générale, je gérer la caméra dans la classe qui implémente l'SurfaceHolder rappels.

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

et puis dans l'Activité:

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

et qui semble fonctionner pour moi. La Rotation des événements provoquent l'Activité à être détruit et recréé, ce qui provoque la SurfaceView à être détruits et recréés.

22voto

validcat Points 1371

Une autre solution simple qui fonctionne bien - pour changer la visibilité de la surface de prévisualisation.

 private SurfaceView preview;
 

la prévisualisation est initiée selon la méthode onCreate . En onResume méthode définie View.VISIBLE pour la surface de prévisualisation:

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

et respectivement en onPause visibilité définie View.GONE :

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

1voto

e7fendy Points 136

SurfaceHolder.Callback est lié à sa surface.

L'activité est-elle à l'écran? Si tel est le cas, il n'y aura pas SurfaceHolder.Callback, car la surface est toujours à l'écran.

Pour contrôler n'importe quelle SurfaceView, vous pouvez la gérer uniquement dans onPause / onResume. Pour SurfaceHolder.Callback, vous pouvez l'utiliser si la surface est modifiée (créée, modifiée, et détruite), comme initialiser openGL avec surfaceCreated et détruire openGL avec surfaceDestroyed, etc.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X