OnPause and OnStop() called immediately after starting activity
Asked Answered
F

7

49

I have an activity that needs to turn screen on(if offed) when it is started. So in onCreate, I have:

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

Using this with help of wakelock in broadcasr receiver , I am able to cause my activity to display whenever it is started from broadcast receiver.

But problem is very strange, activity lifecycle calls in this manner, onPause() and onResume immediately after starting activity

  1. onCreate
  2. onStart
  3. onResume
  4. onPause
  5. onStop
  6. onStart
  7. onResume

So the problem is on start and on resume calling twice, with on stop also calling, I want to implement some logic in onStop() but, with such behavior app will not work correctly.

Edit

I found problem is only due to flag FLAG_SHOW_WHEN_LOCKED. and when device is locked. and it only happens when device is locked before activity is starting.

P.S I am using alarm manager with broadcast receiver, and then starts activity from broadcast receiver.

Foulk answered 18/8, 2014 at 18:37 Comment(6)
amazing question, i have this also with the same flag FLAG_SHOW_WHEN_LOCKED, voted upStepp
I solved it by using a little delay in a handler, that checks in onStopStepp
@Override protected void onStop() { super.onStop(); if (isDeviceSleeping) { if (System.currentTimeMillis() - startTime < 2000) { return; } }Stepp
this could be one way but not proper solutionFoulk
This answer saved me: https://mcmap.net/q/356763/-why-the-onpause-method-is-called-immediately-after-oncreate it's about detecting if the screen is ONLyndsaylyndsey
Did yours work? I am having the same trouble, though I have used wake lock and disable keyguard, it shows the keypad and pauses my activityLuu
R
53
  • Let us understand why the lifecycle methods are called multiple times.

Here is an important code comment documented in ActivityThread, which is responsible for executing the activities of the application process.

We accomplish this by going through the normal startup (because activities expect to go through onResume() the first time they run, before their window is displayed), and then pausing it.

Right after onResume, the activity window is attached to the window manager and onAttachedtoWindow is invoked. If the screen is on, the activity window will get focus and onWindowFocusChanged is invoked with true parameter. From docs:

Keep in mind that onResume is not the best indicator that your activity is visible to the user; a system window such as the keyguard may be in front. Use onWindowFocusChanged(boolean) to know for certain that your activity is visible to the user

In the reported issue, the screen if off. Hence activity window will not get focus, which results in activity's onPause method getting called followed by onStop method, as the activity window is not visible.

Since WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON flag is set on activity window, the window manager service turns on the screen using power manager api. Following is the WindowManagerService code:

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

After the screen turns on onStart and onPause are called again.

Hence : onCreate - onStart - onResume - onPause - onStop - onStart - onPause.

This can be verified by locking the device and starting the activity using adb command or eclipse.

  • Ideal Solution

If you start a task in onCreate you need to stop it in onDestory (if the task is still pending). Similarly for onStart it would be onStop and for onResume it would be onPause.

  • Workaround

If you can't follow the above protocol, you can check the status of activity window focus using hasWindowFocus in onPause method. Normally the activity window focus status will be true in onPause. In scenarios like screen is off or screen is on with keyguard displayed, the activity window focus will be false in 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
    }
}
Rafi answered 24/8, 2014 at 18:20 Comment(5)
Good explanation, but work around is not working, I would rather like to set a flag value in onWindowFocusChanged, and then do handle onPause() and onStop() after window in being focussedFoulk
Why doesn't your workaround use onWindowFocusChanged? On it's surface, that event sounds perfect.Mitchmitchael
Just to add onRestart also gets called after onStop. If anybody is doing some logic inside onStart, just check if activity got restarted, then avoid running logic again in onStartUnplumbed
what about onResume/onStart called multiple times, what's the solution?Summon
tried with Android 14, hasWindowFocus is always false when you open activity from the notification from the lock screenSummon
P
3

Add android:configChanges="keyboardHidden|orientation|screenSize" to you Activity in your Manifest. This may solve your problem.

Pamilapammi answered 24/8, 2014 at 9:34 Comment(1)
How will that solve the problem? Listening for config changes will just prevent the activity from handling the changes automatically. It has nothing to do with screen on/off scenario describedParaffinic
C
3

The workaround by Manish Mulimani worked for me, except I first check for the window focus and then call through to the 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
    }
}
Chrystalchryste answered 14/7, 2015 at 13:40 Comment(0)
K
1

You said you want to implement some logic in onStop() but with such behavior app will not work correctly. you didn’t show us what exactly you have inside onStop() but I think your logic probably restarts the activity.. in that case you Can implement your logic in onStop like that:

@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. other solution that may help you will be to avoid onStop() and use onWindowFocusChanged with 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{
             }
           }
    
Kiri answered 9/8, 2016 at 12:55 Comment(0)
S
0

I've noticed there is activity attribute in the AndroidManifest.xml called android:showOnLockScreen="true|false"

for example:

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

I searched the web for its documentation but no luck, but from its name it should work as the Window flag WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED do.


the only document I've found

Specify that an Activity should be shown over the lock screen and, in a multiuser environment, across all users' windows [boolean]


Edit

can you please try to call your flag code before calling 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);
.
.
.
  }
}
Stepp answered 24/8, 2014 at 12:18 Comment(1)
I believe the reason behind this, is the key-guard screen is stopping the activity at first time, then the activity is resumed againStepp
S
0

Try this. I have used this and it works fine

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();
Sidedress answered 28/8, 2014 at 17:53 Comment(1)
PowerManager.FULL_WAKE_LOCK is deprecatedPropend
D
-1

Use WakeLocker class. It have methods to wake screen on

Detestation answered 18/8, 2014 at 18:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.