开始活动后立即调用OnPause和OnStop()
我有一个活动,当它启动时需要打开屏幕(如果已closures)。 所以在onCreate,我有:
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);
在广播接收机的帮助下使用这个function,我可以使我的活动显示每当它从广播接收机启动。
但是问题很奇怪,活动生命周期以这种方式调用onPause()和onResume之后立即开始活动
- 的onCreate
- 在onStart
- 的onResume
- 在onPause
- 的onStop
- 在onStart
- 的onResume
所以问题是在开始和简历调用两次,停止也调用,我想在onStop()中实现一些逻辑,但与这样的行为应用程序将无法正常工作。
编辑
我发现问题只是由于标志FLAG_SHOW_WHEN_LOCKED。 当设备被locking时。 只有当设备在活动开始之前被locking时才会发生。
PS我使用广播接收器的报警pipe理器,然后从广播接收器开始活动。
- 让我们来了解为什么生命周期方法被多次调用。
下面是一个在ActivityThread中logging的重要代码注释,它负责执行应用程序的活动。
我们通过正常的启动来完成这个任务(因为他们第一次运行onResume()之前,他们的窗口被显示之前他们会经历onResume()),然后暂停它。
在onResume
之后,将活动窗口附加到窗口pipe理器,并调用onAttachedtoWindow
。 如果屏幕打开,则活动窗口将获得焦点,并使用true
参数调用onWindowFocusChanged
。 从文档 :
请记住,onResume不是您的活动对用户可见的最佳指标; 系统窗口如键盘可能在前面。 使用onWindowFocusChanged(boolean)可以确定您的活动对用户是可见的
在报告的问题中,屏幕如果closures。 因此活动窗口不会得到焦点,这导致活动的onPause
方法被调用,接着是onStop
方法,因为活动窗口不可见。
由于在活动窗口上设置了WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
标志,所以窗口pipe理器服务使用电源pipe理API来打开屏幕。 以下是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; } ... }
屏幕打开后, onStart
和onPause
再次被调用。
因此: onCreate - onStart - onResume - onPause - onStop - onStart - onPause
。
这可以通过locking设备并使用adb
命令或eclipse
启动活动来validation。
- 理想解决scheme
如果您在onCreate
启动任务,则需要在onDestory
停止它(如果任务仍未完成)。 同样onStart
它将onStop
和onResume
它将onPause
。
- 解决方法
如果你不能遵循上面的协议,你可以在onPause
方法中使用hasWindowFocus检查活动窗口焦点的状态。 通常,在onPause
,活动窗口焦点状态将为true。 在屏幕closures或显示键盘锁的情况下,在onPause
,活动窗口焦点将为false。
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 } }
在您的Manifest
为您添加android:configChanges="keyboardHidden|orientation|screenSize"
。 这可能会解决你的问题。
我注意到在AndroidManifest.xml
有activity
属性,名为android:showOnLockScreen="true|false"
例如:
<activity android:name="AlarmAlertFullScreen" android:excludeFromRecents="true" android:theme="@style/AlarmAlertFullScreenTheme" android:showOnLockScreen="true" android:screenOrientation="nosensor" android:configChanges="orientation|screenSize|keyboardHidden|keyboard|navigation"/>
我搜查了网页的文档,但没有运气,但从它的名字,它应该作为窗口标志WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
做。
我find的唯一文档
指定应该在locking屏幕上显示一个活动,并在多用户环境中跨所有用户的窗口显示[布尔]
编辑
在调用super.onCreate(...)
之前,您可以请尝试调用您的标志代码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); . . . } }
尝试这个。 我用这个,它工作正常
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();
Manish Mulimani的解决方法为我工作,除了我首先检查窗口焦点,然后调用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 } }
你说你想在onStop()中实现一些逻辑,但这样的行为应用程序将无法正常工作。 你没有向我们展示你在onStop()里面到底是什么,但是我认为你的逻辑可能会重新开始这个活动。在这种情况下,你可以像这样在onStop中实现你的逻辑:
@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) } } } }
-
其他的解决scheme,可能会帮助你将避免onStop()和使用onWindowFocusChanged与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{ } }
使用WakeLocker类。 它有方法来唤醒屏幕