OnPause и OnStop(), вызываемые сразу после запуска активности
У меня есть активность, которая должна включать экран (если он отключен), когда он запущен.
Поэтому в 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);
Используя это с помощью wakelock в приемнике Broadcasr, я могу заставить свою активность отображаться всякий раз, когда она запускается из широковещательного приемника.
Но проблема очень странная, вызовы жизненного цикла активности таким образом, onPause() и onResume сразу после запуска активности
- OnCreate
- OnStart
- onResume
- OnPause
- OnStop
- OnStart
- onResume
Таким образом, проблема заключается в запуске и повторении вызова дважды, причем при остановке также вызывается, я хочу реализовать некоторую логику в onStop(), но при таком поведении приложение не будет работать корректно.
Edit
Я нашел проблему только из-за флага FLAG_SHOW_WHEN_LOCKED. и когда устройство заблокировано. и это происходит только тогда, когда устройство заблокировано до запуска активности.
P.S Я использую диспетчер аварийных сообщений с широковещательным приемником, а затем запускаю активность из широковещательного приемника.
Ответы
Ответ 1
- Поясним, почему методы жизненного цикла вызывают несколько раз.
Вот важный комментарий к коду, зарегистрированный в ActivityThread, который отвечает за выполнение действий процесса приложения.
Мы достигаем этого, пройдя обычный запуск (потому что мероприятия будут проходить через onResume() при первом запуске, перед их отображением), а затем приостановив его.
Сразу после onResume
окно активности прикрепляется к диспетчеру окон и вызывается onAttachedtoWindow
. Если экран включен, окно активности будет получать фокус, а onWindowFocusChanged
вызывается с параметром true
. Из docs:
Имейте в виду, что onResume не лучший индикатор того, что ваш активность видна пользователю; системное окно, такое как блокировка клавиатуры может быть впереди. Использовать onWindowFocusChanged (boolean), чтобы знать наверняка что ваша активность видна пользователю
В опубликованной проблеме экран, если выключен. Следовательно, окно активности не будет получать фокус, в результате чего будет вызываться метод onPause
, за которым следует метод onStop
, поскольку окно активности не отображается.
Поскольку флаг WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
установлен в окне активности, служба диспетчера окон включается на экране с помощью диспетчера питания 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
.
Это можно проверить, заблокировав устройство и запустив его с помощью команды adb
или eclipse
.
Если вы запустите задачу в onCreate
, вам нужно остановить ее в onDestory
(если задача еще не выполнена). Аналогично для onStart
это будет onStop
, а для onResume
это будет onPause
.
Если вы не можете следовать указанному выше протоколу, вы можете проверить статус фокуса окна деятельности, используя hasWindowFocus в методе onPause
. Обычно статус фокуса окна активности будет истинным в onPause
. В таких сценариях, как экран выключен или экран включен с отображением блокировки клавиатуры, фокус окна активности будет ложным в 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
}
}
Ответ 2
Добавьте android:configChanges="keyboardHidden|orientation|screenSize"
к вам Activity
в Manifest
. Это может решить вашу проблему.
Ответ 3
Я заметил, что атрибут activity
в AndroidManifest.xml
называется 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"/>
Я искал в Интернете свою документацию, но не повезло, но из ее имени он должен работать как флаг Window WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
do.
единственный найденный мной документ
Укажите, что действие должно отображаться на экране блокировки и в многопользовательская среда, через окна всех пользователей [boolean]
Edit
попробуйте вызвать код своего флага перед вызовом 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);
.
.
.
}
}
Ответ 4
Попробуйте это. Я использовал это, и он отлично работает
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();
Ответ 5
Обходной путь Маниша Мулимани работал у меня, за исключением того, что я сначала проверяю фокус окна, а затем вызываю 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
}
}
Ответ 6
Вы сказали, что хотите реализовать некоторую логику в 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)
}
}
}
}
-
другое решение, которое может вам помочь, заключается в том, чтобы избежать 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{
}
}
Ответ 7
Использовать класс WakeLocker. У этого есть методы, чтобы разбудить экран на