关于android:OnPause和OnStop()在启动活动后立即调用

OnPause and OnStop() called immediately after starting activity

我有一项活动需要在启动时打开屏幕(如果是关闭)。
所以在onCreate中,我有:

1
2
3
4
5
6
7
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);

在广播接收器的wakelock的帮助下使用它,我能够在广播接收器启动时显示我的活动。

但问题很奇怪,活动生命周期以这种方式调用,onPause()和onResume在启动活动后立即调用

  • 的onCreate
  • 在onStart
  • 的onResume
  • 在onPause
  • 的onStop
  • 在onStart
  • 的onResume
  • 所以问题出在启动和恢复调用两次,在停止也调用,我想在onStop()中实现一些逻辑,但是,这样的行为应用程序将无法正常工作。

    编辑

    我发现问题只是由于标志FLAG_SHOW_WHEN_LOCKED。当设备被锁定时它只会在活动开始前锁定设备时发生。

    P.S我正在使用带有广播接收器的报警管理器,然后从广播接收器开始活动。


    • 让我们理解为什么生命周期方法被多次调用。

    以下是ActivityThread中记录的重要代码注释,它负责执行应用程序进程的活动。

    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.

    onResume之后,活动窗口附加到窗口管理器并调用onAttachedtoWindow。如果屏幕打开,活动窗口将获得焦点,并使用true参数调用onWindowFocusChanged。来自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

    在报告的问题中,屏幕如果关闭。因此,活动窗口不会获得焦点,这会导致活动的onPause方法被调用,然后是onStop方法,因为活动窗口不可见。

    由于在活动窗口上设置了WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON标志,因此窗口管理器服务使用电源管理器api打开屏幕。以下是WindowManagerService代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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;
            }
            ...
    }

    屏幕打开后,再次调用onStartonPause

    因此:onCreate - onStart - onResume - onPause - onStop - onStart - onPause

    这可以通过使用adb命令或eclipse锁定设备并启动活动来验证。

    • 理想解决方案

    如果您在onCreate中启动任务,则需要在onDestory中将其停止(如果任务仍处于待处理状态)。类似地,对于onStart,它将是onStop,对于onResume,它将是onPause

    • 解决方法

    如果您不能遵循上述协议,则可以使用onPause方法中的hasWindowFocus检查活动窗口焦点的状态。通常,活动窗口焦点状态在onPause中为真。在屏幕关闭或屏幕打开且显示键控的情况下,活动窗口焦点在onPause中将为false。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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
        }
    }


    Manish Mulimani的解决方法为我工作,除了我首先检查窗口焦点然后调用super.onPause():

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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
        }
    }


    你说你想在onStop()中实现一些逻辑但是这样的行为app将无法正常工作。你没有告诉我们你在onStop()中究竟有什么,但我认为你的逻辑可能会重新开始活动..
    在这种情况下,您可以在onStop中实现您的逻辑:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @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)
                    }
                }
            }
        }
  • 其他解决方案可能会帮助你避免onStop()并使用onWindowFocusChanged与bool hasFocus

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
      /**
      * 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{
             }
           }

  • Manifest中将android:configChanges="keyboardHidden|orientation|screenSize"添加到Activity。这可以解决您的问题。


    试试这个。我用过这个,它运行正常

    1
    2
    3
    4
    5
    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();


    我注意到AndroidManifest.xml中的Activity属性名为android:showOnLockScreen="true|false"

    例如:

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

    我在网上搜索了它的文档,但没有运气,但从它的名字来看,它应该像Window标志WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED那样工作。

    我发现的唯一文件

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

    编辑

    你可以在调用super.onCreate(...)之前尝试调用你的标志代码吗?

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    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);
    .
    .
    .
      }
    }


    使用WakeLocker类。它有唤醒屏幕的方法