web-dev-qa-db-ja.com

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

これをbroadcasrレシーバーのwakelockの助けを借りて使用すると、ブロードキャストレシーバーから開始されるたびにアクティビティを表示することができます。

しかし、問題は非常に奇妙です。アクティビティのライフサイクルは、アクティビティを開始した直後にこの方法でonPause()およびonResumeを呼び出します

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

したがって、問題は開始時と再開時の2回の呼び出しであり、停止時の呼び出しもあるため、onStop()にロジックを実装したいのですが、このような動作ではアプリは正しく動作しません。

編集

問題はフラグFLAG_SHOW_WHEN_LOCKEDのみが原因であることがわかりました。デバイスがロックされているとき。アクティビティが開始される前にデバイスがロックされている場合にのみ発生します。

追伸:ブロードキャストレシーバーでアラームマネージャーを使用してから、ブロードキャストレシーバーからアクティビティを開始します。

42
Haris
  • ライフサイクルメソッドが複数回呼び出される理由を理解しましょう。

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

画面がオンになった後、onStartonPauseが再度呼び出されます。

したがって:onCreate - onStart - onResume - onPause - onStop - onStart - onPause

これは、デバイスをロックし、adbコマンドまたはEclipseを使用してアクティビティを開始することで確認できます。

  • 理想的な解決策

onCreateでタスクを開始する場合は、onDestoryで停止する必要があります(タスクがまだ保留中の場合)。同様に、onStartの場合はonStopになり、onResumeの場合はonPauseになります。

  • 回避策

上記のプロトコルに従わない場合は、onPauseメソッドで hasWindowFocus を使用して、アクティビティウィンドウのフォーカスのステータスを確認できます。通常、アクティビティウィンドウのフォーカスステータスはonPauseでtrueになります。画面がオフまたは画面がオンで、キーガードが表示されているシナリオでは、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
    }
}
45
Manish Mulimani

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
    }
}
2
cyberhuman

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()を避け、bool hasFocusでonWindowFocusChangedを使用するのに役立つ他のソリューション

      /**
      * 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{
             }
           }
    
1
Udi Reshef

追加 Android:configChanges="keyboardHidden|orientation|screenSize"あなたへActivityManifestに。これで問題が解決する場合があります。

1
Atif Farrukh

_AndroidManifest.xml_に_Android:showOnLockScreen="true|false"_というactivity属性があることに気づきました

例えば:

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

Webでドキュメントを検索しましたが、運はありませんでしたが、その名前からは、Windowフラグ_WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED_ doとして機能するはずです。


私が見つけた唯一の文書

アクティビティをロック画面に表示し、マルチユーザー環境ではすべてのユーザーのウィンドウに表示することを指定します[ブール]


編集

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);
.
.
.
  }
}
_
0
Mohammad Ersan

これを試して。私はこれを使用しましたが、うまくいきます

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