Подтвердить что ты не робот

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 Я использую диспетчер аварийных сообщений с широковещательным приемником, а затем запускаю активность из широковещательного приемника.

4b9b3361

Ответ 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)
                }
            }
        }
    }
  1. другое решение, которое может вам помочь, заключается в том, чтобы избежать 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. У этого есть методы, чтобы разбудить экран на