ライフサイクルコールバックのsuperについて、自分の実装の先に呼ぶか、後に呼ぶか
ライフサイクルコールバックのsuperについて、自分の実装の先に呼ぶか、後に呼ぶかというのが気になっていて、
というStackOverflowを追いかけてみました。
まとめ
- Googleは
Always call the superclass method first
と書いているが、必ずしもそうではなさそう - onDestroy()など、コンポーネントを停止/解体するメソッドではActivityのリソースの解放などを行うので、自身の処理がそれらに依存する場合は、自身の処理を先に
- onStart()など、コンポーネントを生成/起動するメソッドではActivityのリソースの準備を行うので、自身の処理がそれらに依存する場合は、自身の処理を後に
- Themeの挙動などを標準のものと変えたい場合は、その変更処理をsuperの先に置く場合もあるのでは
メモ
和訳じゃないです。
上記のリンク先で質問者は、
@Override protected void onStop() { super.onStop(); // Always call the superclass method first //my implementation here }
@Override protected void onStop() { //my implementation here super.onStop(); }
の2つの書き方のどちらがしたらよいのかと聞いています。
一番投票を得ている人の回答を追ってみます。
- Googleとしては、
Always call the superclass method first
と書いています - Javaとしては、特に
superclass method
を先に呼ばねばならない、とは決められていないし、呼ぶこと自体も決められていないようです- ただし、Activityのサブクラスの場合は、下記のコードにおいて、superclassのメソッドを呼ぶことを求められています
- AndroidのActivity.javaのコードで確認したら、
performStart
,performRestart
,performResume
,performPause
,performStop
,でSuperNotCalledException
を投げている行がありました。onCreateでもsuperを呼ばないと、setContentViewでぬるぽが発生します。onCreate/onDestroyでViewをwindowに足したり削除(detach)したり、といった処理を担当しているようです
- AndroidのActivity.javaのコードで確認したら、
- しかし、このこと自体は順番の話には関係ないですね
- ただし、Activityのサブクラスの場合は、下記のコードにおいて、superclassのメソッドを呼ぶことを求められています
if (!mCalled) { throw new SuperNotCalledException( "Activity " + mComponent.toShortString() + " did not call through to super.onStop()"); }
- onPauseやonDestroyはどちらの順番でも動いているように見えますが...
- onPauseの実装を見てみます
- 下記の実装なら、どちらの順番で先に読んでも良さそうでしょうか?
protected void onPause() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this); // This is to invoke // Application.ActivityLifecyleCallbacks.onActivityPaused(Activity) getApplication().dispatchActivityPaused(this); // The flag to enforce calling of this method mCalled = true; }
すこしだけ、super内で呼ばれてそうなメソッド名で検索かけた結果、state.resumed = false;
の行だけFragment関係*1で気になりますが...まあ、大丈夫そうな気もします?
/android/app/Application.java: 56 void onActivityStarted(Activity activity); 57 void onActivityResumed(Activity activity); 58: void onActivityPaused(Activity activity); 59 void onActivityStopped(Activity activity); 60 void onActivitySaveInstanceState(Activity activity, Bundle outState); .. 215 if (callbacks != null) { 216 for (int i=0; i<callbacks.length; i++) { 217: ((ActivityLifecycleCallbacks)callbacks[i]).onActivityPaused(activity); 218 } 219 } /android/nfc/NfcActivityManager.java: 440 /** Callback from Activity life-cycle, on main thread */ 441 @Override 442: public void onActivityPaused(Activity activity) { 443 boolean readerModeFlagsSet; 444 Binder token; synchronized (NfcActivityManager.this) { NfcActivityState state = findActivityState(activity); if (DBG) Log.d(TAG, "onPause() for " + activity + " " + state); if (state == null) return; state.resumed = false; token = state.token; readerModeFlagsSet = state.readerModeFlags != 0; } if (readerModeFlagsSet) { // Restore default p2p modes setReaderMode(token, 0, null); } } /android/print/PrintManager.java: 635 636 @Override 637: public void onActivityPaused(Activity activity) { 638 /* do nothing */ 639 }
- Activity.onStop()も同様の内容で...
- むしろ、Activity.onStop()の方がActionBarなどのコールバック以外、上に乗っているコンポーネントに影響するような状態が変化するようなことはしてなさそうです。
protected void onStop() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this); if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false); mActivityTransitionState.onStop(); getApplication().dispatchActivityStopped(this); mTranslucentCallback = null; mCalled = true; }
/android/app/Application.java: 57 void onActivityResumed(Activity activity); 58 void onActivityPaused(Activity activity); 59: void onActivityStopped(Activity activity); 60 void onActivitySaveInstanceState(Activity activity, Bundle outState); 61 void onActivityDestroyed(Activity activity); .. 224 if (callbacks != null) { 225 for (int i=0; i<callbacks.length; i++) { 226: ((ActivityLifecycleCallbacks)callbacks[i]).onActivityStopped(activity); 227 } 228 } /android/nfc/NfcActivityManager.java: 459 /** Callback from Activity life-cycle, on main thread */ 460 @Override 461: public void onActivityStopped(Activity activity) { /* NO-OP */ } 462 463 /** Callback from Activity life-cycle, on main thread */ /android/print/PrintManager.java: 655 656 @Override 657: public void onActivityStopped(Activity activity) { 658 /* do nothing */ 659 }
- onDestroy()を見てみましょう
- 利用しているリソースの解放などを行っているみたいです
Always call the superclass method first
は必ずしも保証のあるものではないかもですね- そのリソースに関して、自分が書いた初期化処理を行いたいのであれば、super.onDestroy()の前に行うべきでは
protected void onDestroy() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this); mCalled = true; // dismiss any dialogs we are managing. if (mManagedDialogs != null) { final int numDialogs = mManagedDialogs.size(); for (int i = 0; i < numDialogs; i++) { final ManagedDialog md = mManagedDialogs.valueAt(i); if (md.mDialog.isShowing()) { md.mDialog.dismiss(); } } mManagedDialogs = null; } // close any cursors we are managing. synchronized (mManagedCursors) { int numCursors = mManagedCursors.size(); for (int i = 0; i < numCursors; i++) { ManagedCursor c = mManagedCursors.get(i); if (c != null) { c.mCursor.close(); } } mManagedCursors.clear(); } // Close any open search dialog if (mSearchManager != null) { mSearchManager.stopSearch(); } getApplication().dispatchActivityDestroyed(this); }
回答者がまとめている他の人の意見のまとめから一部個人的に重複していると思うものを除いて抜粋すると、
- super.onPause()の後では、
setResult(...)
が動かないことがあるので、startActivityForResult
を利用する場合は、super.onPause()
を後回しに - Activityクラスで扱うリソースにロジックが依存していないのならば、superをどの順で扱っても良い
- が、実際
super.onDestroy()
の場所はロジックに影響を与えるのでは
- が、実際
- onCreate(), onStart(), onResume(), etc.などコンポーネントを作る方のメソッドではsuperを先に、onPause(), onStop(), onDestroy(), etc.など、コンポーネントを解体する方のメソッドでは、superを後に。
- テーマの設定など、Activityの標準的な挙動に影響を与えたい場合は、superのメソッドを呼び出す前にする
でした。