web-dev-qa-db-fra.com

OnPause et OnStop () appelés immédiatement après le début de l'activité

J'ai une activité qui doit activer l'écran (si cette option est activée) au démarrage . Donc, dans onCreate, j'ai:

this.getWindow().setFlags(
            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);

En utilisant ceci avec l’aide de wakelock dans le récepteur broadcasr, je peux faire en sorte que mon activité s’affiche à chaque démarrage depuis le récepteur broadcast.

Mais le problème est très étrange, le cycle de vie d'une activité appelle de cette manière, onPause () et onResume immédiatement après le début de l'activité.

  1. onCreate
  2. onStart
  3. pour résumer
  4. onPause
  5. onStop
  6. onStart
  7. pour résumer

Donc, le problème est au début et à la reprise appelant deux fois, avec sur arrêter d'appeler également, je veux implémenter une logique dans onStop () mais, avec un tel comportement app ne fonctionnera pas correctement. 

Modifier

J'ai trouvé le problème est dû uniquement à l'indicateur FLAG_SHOW_WHEN_LOCKED. et quand l'appareil est verrouillé. et cela ne se produit que lorsque le périphérique est verrouillé avant le début de l'activité.

P.S J'utilise le gestionnaire d'alarmes avec le récepteur de diffusion, puis commence l'activité à partir du récepteur de diffusion.

39
Haris
  • Laissez-nous comprendre pourquoi les méthodes de cycle de vie sont appelées plusieurs fois.

Voici un commentaire de code important documenté dans ActivityThread , responsable de l'exécution des activités du processus d'application.

Nous accomplissons cela en passant par le démarrage normal (car les activités S'attendent à passer par onResume () lors de leur première exécution, Avant que leur fenêtre ne soit affichée), puis en la mettant en pause.

Juste après onResume, la fenêtre d'activité est attachée au gestionnaire de fenêtres et onAttachedtoWindow est invoquée. Si l'écran est allumé, la fenêtre d'activité devient active et onWindowFocusChanged est appelé avec le paramètre true. De docs :

Gardez à l’esprit que onResume n’est pas le meilleur indicateur que votre l'activité est visible pour l'utilisateur; une fenêtre système telle que le protège-clavier peut être en face. Utilisez onWindowFocusChanged (boolean) pour en savoir plus que votre activité est visible pour l'utilisateur

Dans le problème signalé, l'écran est désactivé. Par conséquent, la fenêtre d'activité ne sera pas activée, ce qui entraînera l'appel de la méthode onPause de l'activité suivie de la méthode onStop, car la fenêtre d'activité n'est pas visible.

Étant donné que l'indicateur WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON est défini dans la fenêtre d'activité, le service du gestionnaire de fenêtres active l'écran à l'aide de l'interface du gestionnaire d'alimentation. Voici le code WindowManagerService :

public int relayoutWindow(...) {
    ...
    toBeDisplayed = !win.isVisibleLw();
    ...
    if (toBeDisplayed) {
        ...
        if ((win.mAttrs.flags
            & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
            if (DEBUG_VISIBILITY) Slog.v(TAG,
                "Relayout window turning screen on: " + win);
                win.mTurnOnScreen = true;
            }
        ...
        if (mTurnOnScreen) {
            if (DEBUG_VISIBILITY) Slog.v(TAG, "Turning screen on after layout!");
            mPowerManager.wakeUp(SystemClock.uptimeMillis());
            mTurnOnScreen = false;
        }
        ...
}

Une fois l’écran allumé, onStart et onPause sont à nouveau appelés.

D'où: onCreate - onStart - onResume - onPause - onStop - onStart - onPause

Cela peut être vérifié en verrouillant le périphérique et en démarrant l'activité à l'aide de la commande adb ou de Eclipse

  • Solution idéale

Si vous démarrez une tâche dans onCreate, vous devez l'arrêter dans onDestory (si la tâche est toujours en attente). De même pour onStart ce serait onStop et pour onResume ce serait onPause

  • Solution de contournement 

Si vous ne pouvez pas suivre le protocole ci-dessus, vous pouvez vérifier l'état du focus de la fenêtre d'activité à l'aide de la méthode hasWindowFocus in onPause. Normalement, l’état du focus de la fenêtre d’activité sera true dans onPause. Dans des scénarios tels que l'écran est éteint ou l'écran est allumé avec le verrouillage du clavier affiché, le focus de la fenêtre d'activité sera faux dans onPause.

boolean mFocusDuringOnPause;

public void onPause() {
    super.onPause;

    mFocusDuringOnPause = hasWindowFocus();    
}

public void onStop() {
    super.onStop();

    if(mFocusDuringOnPause) {
        // normal scenario
    } else {
        // activity was started when screen was off / screen was on with keygaurd displayed
    }
}
41
Manish Mulimani

Vous avez dit vouloir implémenter une logique dans onStop () mais avec un tel comportement, l'application ne fonctionnerait pas correctement. vous ne nous avez pas montré ce que vous avez exactement dans onStop () mais je pense que votre logique relance probablement l’activité .. dans ce cas, vous pouvez implémenter votre logique dans onStop de la manière suivante:

@Override
protected void onStop(){
    super.onStop();

         //implement your code only if !STATE_OFF - to prevent infinite loop onResume <-> onStop  while screen is off                      
        DisplayManager dm = (DisplayManager) this.getSystemService(Context.DISPLAY_SERVICE);
        for (Display display : dm.getDisplays()){
               if(display.getState() != Display.STATE_OFF){

                 //implement your code only if device is not in "locked"  state     
                KeyguardManager myKM = (KeyguardManager) this.getSystemService(Context.KEYGUARD_SERVICE);
                if( !myKM.inKeyguardRestrictedInputMode())      
                    //If needed you can call finish() (call onDestroy()) and your logic will restart your activity only once.             
                    finish();                  
                    // implement your logic here (your logic probably restarts the activity)
                }
            }
        }
    }
  1. une autre solution qui pourrait vous aider sera d'éviter onStop () et d'utiliser onWindowFocusChanged avec bool hasFocus. 

      /**
      * Called when the current {@link Window} of the activity gains or loses
      * focus.  This is the best indicator of whether this activity is visible
      * to the user.
      */
        @Override
           public void onWindowFocusChanged(boolean hasFocus){
             super.onWindowFocusChanged(hasFocus);
             if( ! hasFocus  ){
             }
             else{
             }
           }
    
1
Udi Reshef

Ajoutez Android:configChanges="keyboardHidden|orientation|screenSize" à Activity dans votre Manifest. Cela peut résoudre votre problème.

1
Atif Farrukh

La solution de rechange de Manish Mulimani a fonctionné pour moi, sauf que je vérifie d’abord le focus de la fenêtre, puis que je passe au super.onPause ():

public void onPause() {
    mFocusDuringOnPause = hasWindowFocus();    
    super.onPause();
}

public void onStop() {
    super.onStop();
    if (mFocusDuringOnPause) {
        // normal scenario
    } else {
        // activity was started when screen was off or screen was on with keyguard displayed
    }
}
1
cyberhuman

J'ai remarqué qu'il y a un attribut activity dans le AndroidManifest.xml appelé Android:showOnLockScreen="true|false"

par exemple:

   <activity Android:name="AlarmAlertFullScreen"
                Android:excludeFromRecents="true"
                Android:theme="@style/AlarmAlertFullScreenTheme"
                Android:showOnLockScreen="true"
                Android:screenOrientation="nosensor"
                Android:configChanges="orientation|screenSize|keyboardHidden|keyboard|navigation"/>

J'ai cherché sur le Web sa documentation, mais pas de chance, mais d'après son nom, il devrait fonctionner comme le drapeau Window WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED do.


le seul document que j'ai trouvé

Spécifiez qu'une activité doit être affichée sur l'écran de verrouillage et, dans un environnement multi-utilisateur, à travers les fenêtres de tous les utilisateurs [booléen]


Modifier

pouvez-vous s'il vous plaît essayer d'appeler votre code de drapeau avant d'appeler super.onCreate(...)

public class BaseActivity extends Activity {

    @Override
    protected void onCreate(Bundle bundle) {
    this.getWindow().setFlags(
            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON,
            WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                    | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                    | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
        //then call super
        super.onCreate(bundle);
.
.
.
  }
}
0
Mohammad Ersan

Essaye ça. J'ai utilisé ça et ça marche bien

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);

        _wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
            POWER_SERVICE);
    _wakeLock.acquire();
0
Ajit Pratap Singh