アクティビティスタックのライフサイクル(パート2)

記事の最初の部分で合意したように、これでは、アクティビティスタックの標準的な動作を変更するためのツールを検討します。



今日のトピックに関する理論全体は、 developer.android.com /guide/topics/manifest/activity-element.htmlにあります。いくつかの場所でそれを参照し、実際にどのように機能するかを理解し、どのような状況でそれを見つけようとします実際に使用できます。



AndroidManifestとコード内のIntentフラグの両方で、いくつかのパラメーターを追加できます。

intent.setFlags(Intent.FLAG_ACTIVITY_*);
      
      





android:launchMode



パラメータは、特定のアクティビティへの呼び出しで新しいインテントをアクティブにしたときに何が起こるかを決定します。

この例では、ActivityAに適用されます。



「標準」および「シングルトップ」(FLAG_ACTIVITY_SINGLE_TOP)


"standard"



がデフォルトの動作です。 システムは常に新しいアクティビティを作成し、スタックの一番上に追加します。

ActivityAを変更して、ActivityBに切り替えるのではなく、自動的に起動するようにします。



( "標準") A-> A->戻る->戻る:

 ***   *** INFO/ActivityManager(249): START {flg=0x10000000 cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 28371 INFO/ActivityManager(249): Start proc com.habrahabr.ActivityStackLifeCycle for activity com.habrahabr.ActivityStackLifeCycle/.ActivityA: pid=28410 uid=10060 gids={1028} DEBUG/ActivityA(28410): onCreate() DEBUG/ActivityA(28410): onStart() INFO/ActivityManager(249): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityA: +2s64ms ***  ActivityA *** INFO/ActivityManager(249): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 28410 DEBUG/ActivityA(28410): onCreate() DEBUG/ActivityA(28410): onStart() INFO/ActivityManager(249): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityA: +739ms DEBUG/ActivityA(28410): onStop() ***  back *** DEBUG/ActivityA(28410): onStart() DEBUG/ActivityA(28410): onStop() DEBUG/ActivityA(28410): onDestroy() ***  back *** DEBUG/ActivityA(28410): onStop() DEBUG/ActivityA(28410): onDestroy() ***   Home screen***
      
      





スタックには2つの同一のアクティビティがあり、2回のバッククリック後にプロセスが終了したことがわかります。



"singleTop"



は、再度呼び出されたときにスタックの最上部にあるアクティビティの重複を防ぎます。



( "SingleTop") A-> A->戻る:

 ***   *** INFO/ActivityManager(249): START {flg=0x10000000 cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 31016 INFO/ActivityManager(249): Start proc com.habrahabr.ActivityStackLifeCycle for activity com.habrahabr.ActivityStackLifeCycle/.ActivityA: pid=31070 uid=10060 gids={1028} DEBUG/ActivityA(31070): onCreate() DEBUG/ActivityA(31070): onStart() INFO/ActivityManager(249): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityA: +1s296ms ***  ActivityA *** INFO/ActivityManager(249): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 31070 DEBUG/ActivityA(31070): onNewIntent() ***  back *** DEBUG/ActivityA(31070): onStop() DEBUG/ActivityA(31070): onDestroy() ***   Home screen***
      
      





新しいアクティビティは作成されず、代わりにonNewIntent()



呼び出しがありました。 最初のバックでは、アプリケーションを離れました。



「SingleTask」および「singleInstance」


"singleTask"



および"singleInstance"



は、同じアクティビティの複数のエンティティを許可しません。 これらは、タスクで他のアクティビティを使用する能力が異なります。



( "SingleTask") A-> B-> C-> A->戻る:

 ***   *** INFO/ActivityManager(249): START {flg=0x10000000 cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 1496 INFO/ActivityManager(249): Start proc com.habrahabr.ActivityStackLifeCycle for activity com.habrahabr.ActivityStackLifeCycle/.ActivityA: pid=1529 uid=10060 gids={1028} DEBUG/ActivityA(1529): onCreate() DEBUG/ActivityA(1529): onStart() INFO/ActivityManager(249): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityA: +1s769ms ***  ActivityB *** INFO/ActivityManager(249): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityB u=0} from pid 1529 DEBUG/ActivityB(1529): onCreate() DEBUG/ActivityB(1529): onStart() INFO/ActivityManager(249): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityB: +524ms DEBUG/ActivityA(1529): onStop() ***  ActivityC *** INFO/ActivityManager(249): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityC u=0} from pid 1529 DEBUG/ActivityC(1529): onCreate() DEBUG/ActivityC(1529): onStart() INFO/ActivityManager(249): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityC: +267ms DEBUG/ActivityB(1529): onStop() ***  ActivityA *** DEBUG/ActivityB(1529): onDestroy() DEBUG/ActivityA(1529): onNewIntent() DEBUG/ActivityA(1529): onStart() DEBUG/ActivityC(1529): onStop() DEBUG/ActivityC(1529): onDestroy() ***  back *** 11-13 00:08:00.039: DEBUG/ActivityA(1529): onStop() 11-13 00:08:00.039: DEBUG/ActivityA(1529): onDestroy() ***   Home screen***
      
      





再びActivityAに切り替えると、システムはスタック内でその上にあったすべてのアクティビティを破棄しました。 押すとホーム画面に戻ります。



( "SingleInstance") A-> B-> C-> A-> back-> back-> back:

 ***   *** 11-13 00:12:27.132: INFO/ActivityManager(249): START {flg=0x10000000 cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 2418 11-13 00:12:27.859: INFO/ActivityManager(249): Start proc com.habrahabr.ActivityStackLifeCycle for activity com.habrahabr.ActivityStackLifeCycle/.ActivityA: pid=2438 uid=10060 gids={1028} 11-13 00:12:28.332: DEBUG/ActivityA(2438): onCreate() 11-13 00:12:28.457: DEBUG/ActivityA(2438): onStart() 11-13 00:12:29.254: INFO/ActivityManager(249): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityA: +1s606ms ***  ActivityB *** 11-13 00:12:32.195: INFO/ActivityManager(249): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityB u=0} from pid 2438 11-13 00:12:32.679: DEBUG/ActivityB(2438): onCreate() 11-13 00:12:32.824: DEBUG/ActivityB(2438): onStart() 11-13 00:12:33.394: INFO/ActivityManager(249): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityB: +897ms 11-13 00:12:33.547: DEBUG/ActivityA(2438): onStop() ***  ActivityC *** 11-13 00:12:36.257: INFO/ActivityManager(249): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityC u=0} from pid 2438 11-13 00:12:36.507: DEBUG/ActivityC(2438): onCreate() 11-13 00:12:36.582: DEBUG/ActivityC(2438): onStart() 11-13 00:12:37.343: INFO/ActivityManager(249): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityC: +989ms 11-13 00:12:37.695: DEBUG/ActivityB(2438): onStop() ***  ActivityA *** 11-13 00:12:38.660: INFO/ActivityManager(249): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 2438 11-13 00:12:38.734: DEBUG/ActivityA(2438): onNewIntent() 11-13 00:12:38.734: DEBUG/ActivityA(2438): onStart() 11-13 00:12:39.789: DEBUG/ActivityC(2438): onStop() ***  back *** 11-13 00:12:41.425: DEBUG/ActivityC(2438): onStart() 11-13 00:12:42.250: DEBUG/ActivityA(2438): onStop() 11-13 00:12:42.250: DEBUG/ActivityA(2438): onDestroy() ***  back *** 11-13 00:12:52.332: DEBUG/ActivityB(2438): onStart() 11-13 00:12:52.894: DEBUG/ActivityC(2438): onStop() 11-13 00:12:52.898: DEBUG/ActivityC(2438): onDestroy() ***  back *** 11-13 00:12:55.617: DEBUG/ActivityB(2438): onStop() 11-13 00:12:55.617: DEBUG/ActivityB(2438): onDestroy() ***   Home screen***
      
      





ActivityAに切り替えてもチェーンリアクションはトリガーされませんでしたが、1つのActivityAで別のタスクが開かれました。 戻るの最初のプレスで完成しました。 さらに2回クリックするだけで、ホーム画面に入ることができます。 唯一のエンティティActivityAは上で破壊され、そこへの戻りはありませんでした。 外部的には、ActivityAからActivityBへ、およびActivityCからActivityAへの移行(つまり、同じプロセス内の異なるタスク間の移行)は、アプリケーションの変更、つまり 1つのアクティビティを折りたたみ、よりスムーズな遷移ではなく、新しいアクティビティから飛び出します。



android:noHistory(FLAG_ACTIVITY_NO_HISTORY)



デフォルト値はfalse



です。 true



場合、停止したアクティビティに戻ることはできません。

パラメーターは、値true



ActivityAに適用されtrue





 <activity android:name=".ActivityA" android:noHistory="true">
      
      





A-> B->戻る:

 ***   *** INFO/ActivityManager(249): START {flg=0x10000000 cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 4875 INFO/ActivityManager(249): Start proc com.habrahabr.ActivityStackLifeCycle for activity com.habrahabr.ActivityStackLifeCycle/.ActivityA: pid=4915 uid=10060 gids={1028} DEBUG/ActivityA(4915): onCreate() DEBUG/ActivityA(4915): onStart() INFO/ActivityManager(249): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityA: +1s383ms ***  ActivityB *** INFO/ActivityManager(249): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityB u=0} from pid 4915 DEBUG/ActivityB(4915): onCreate() DEBUG/ActivityB(4915): onStart() INFO/ActivityManager(249): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityB: +877ms DEBUG/ActivityA(4915): onStop() ***  back *** DEBUG/ActivityA(4915): onDestroy() DEBUG/ActivityB(4915): onStop() DEBUG/ActivityB(4915): onDestroy() ***   Home screen***
      
      





onDestroy()



ActivityAで起動された瞬間から判断すると、ActivityAが呼び出された後でもメモリに残っていました。 onStop()



。ただし、そこに戻ることはできなくなりました。



このパラメーターは、アプリケーションの起動時にロゴを表示する必要があり、そのロゴに戻らない場合に使用すると便利です。



android:clearTaskOnLaunch and android:finishOnTaskLaunch



clearTaskOnLaunch



パラメーターclearTaskOnLaunch



true



場合、アプリケーションの再起動時に、スタック(または特定のタスク)上のすべての非ルートアクティビティを破棄するようシステムにclearTaskOnLaunch



ます。 ルートアクティビティのみに適用するのは理にかなっているため、ログの取得元の例では、ActivityAに追加しました。

 <activity android:name=".ActivityA" android:clearTaskOnLaunch="true">
      
      





アプリの開始-> A-> B-> C->ホーム->アプリの開始:

 ***   *** INFO/ActivityManager(250): START {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 476 DEBUG/ActivityA(3412): onCreate() DEBUG/ActivityA(3412): onStart() INFO/ActivityManager(250): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityA: +295ms ***  ActivityB *** INFO/ActivityManager(250): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityB u=0} from pid 3412 DEBUG/ActivityB(3412): onCreate() DEBUG/ActivityB(3412): onStart() INFO/ActivityManager(250): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityB: +140ms DEBUG/ActivityA(3412): onStop() ***  ActivityC *** INFO/ActivityManager(250): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityC u=0} from pid 3412 DEBUG/ActivityC(3412): onCreate() DEBUG/ActivityC(3412): onStart() INFO/ActivityManager(250): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityC: +131ms DEBUG/ActivityB(3412): onStop() ***  Home *** INFO/ActivityManager(250): START {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10200000 cmp=com.android.launcher/com.android.launcher2.Launcher u=0} from pid 250 DEBUG/ActivityC(3412): onStop() ***       *** INFO/ActivityManager(250): START {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 476 DEBUG/ActivityC(3412): onDestroy() DEBUG/ActivityB(3412): onDestroy() DEBUG/ActivityA(3412): onStart()
      
      





アプリケーションを再起動すると、Androidが子ActivityBとActivityCをメモリから破壊したことがわかります。 また、Recentsメニュー(ホームボタンを長押し)からアプリケーションに戻ってもIntent LAUNCHERが開始されないため、ActivityCに戻ることになります。



finishTaskOnLaunch



パラメーターを使用して、 finishTaskOnLaunch



同じ動作を実現できます。 このパラメーターの値がtrue



であるアプリケーションを再起動すると、Androidはこれらのアクティビティを破棄しtrue



。 つまり この例では、同じログを表示するには、ActivityBとActivityCに登録するだけで十分です。

 <activity android:name=".ActivityB" android:finishOnTaskLaunch="true"/> <activity android:name=".ActivityC" android:finishOnTaskLaunch="true"/>
      
      





デフォルトでは、両方のパラメーターはfalse



です。



考えられる使用例の1つは、 excludeFromRecents



パラメーター(RecentsメニューにActivityを含まない)と組み合わせて、停止したActivityに戻れないことを実現することです。 しかし、もっと具体的な、または逆に単純なケースがあると思います。



android:parentActivityName



このパラメーターを使用すると、特定のアクティビティの親を必要に応じて作成できます。 ただし、戻るボタンではなく、ナビゲーションアップ(http://developer.android.com/training/implementing-navigation/ancestral.html)によって、たとえばアクションバーのように、そこに戻ることに注意してください。 。 しかし、気にしないために、ActivityCでonBackPressed()



を再定義し、ActivityAをActivityCの親にします。

例:

 @Override public void onBackPressed() { onNavigateUp(); }
      
      





 <activity android:name=".ActivityC" android:parentActivityName=".ActivityA"> <!-- Parent activity meta-data to support 4.0 and lower --> <meta-data android:name="android.support.PARENT_ACTIVITY" android:value=".ActivityA" /> </activity>
      
      





A-> B-> C->戻る:

 ***   *** INFO/ActivityManager(250): START {flg=0x10000000 cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 6620 INFO/ActivityManager(250): Start proc com.habrahabr.ActivityStackLifeCycle for activity com.habrahabr.ActivityStackLifeCycle/.ActivityA: pid=6634 uid=10060 gids={1028} DEBUG/ActivityA(6634): onCreate() DEBUG/ActivityA(6634): onStart() INFO/ActivityManager(250): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityA: +895ms ***  ActivityB *** INFO/ActivityManager(250): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityB u=0} from pid 6634 DEBUG/ActivityB(6634): onCreate() DEBUG/ActivityB(6634): onStart() INFO/ActivityManager(250): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityB: +179ms DEBUG/ActivityA(6634): onStop() ***  ActivityC *** INFO/ActivityManager(250): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityC u=0} from pid 6634 DEBUG/ActivityC(6634): onCreate() DEBUG/ActivityC(6634): onStart() INFO/ActivityManager(250): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityC: +144ms DEBUG/ActivityB(6634): onStop() ***  back *** INFO/ActivityManager(250): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 6634 DEBUG/ActivityB(6634): onDestroy() DEBUG/ActivityA(6634): onDestroy() DEBUG/ActivityA(6634): onCreate() DEBUG/ActivityA(6634): onStart() INFO/ActivityManager(250): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityA: +182ms DEBUG/ActivityC(6634): onStop() DEBUG/ActivityC(6634): onDestroy()
      
      





クリックして戻ると、予想以上のことが起こったことがわかります。 破壊されたのは親の上にあったアクティビティだけではなく、彼女自身も再現されました。 ただし、全体的には動作が予想されます。



ユーザーが、たとえば戻るボタンに何度も戻ることなく子アクティビティについて長い間さまよい歩いた後、メインメニューに戻ることができるようにするために使用するのが合理的です(予想どおり、アクションバーで実装する場合)。



android:allowTaskReparenting and android:taskAffinity



allowTaskReparenting



パラメーターをallowTaskReparenting



と、タスク#2で以前に作成された(つまり、それに関連付けられている)タスク#1から呼び出されたアクティビティをタスク#1にバインドallowTaskReparenting





準備:

 <activity android:name=".ActivityA" android:launchMode="singleInstance" > <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".ActivityB" android:launchMode="singleTask" /> <activity android:name=".ActivityC" android:launchMode="singleTask" android:allowTaskReparenting="true" android:taskAffinity=".ActivityA" />
      
      





ActivityCを開始するボタンをActivityAフォームにもう1つ追加します。



マニフェストファイルでは、ActivityAが親であると主張する場合、ActivityCが親を変更することを許可しました。



アプリの開始-> A-> B-> C->ホーム->アプリの開始-> A-> C->戻る:

 ***   *** INFO/ActivityManager(250): START {flg=0x10000000 cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 10495 INFO/ActivityManager(250): Start proc com.habrahabr.ActivityStackLifeCycle for activity com.habrahabr.ActivityStackLifeCycle/.ActivityA: pid=10524 uid=10060 gids={1028} DEBUG/ActivityA(10524): onCreate() DEBUG/ActivityA(10524): onStart() INFO/ActivityManager(250): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityA: +761ms ***  ActivityB *** INFO/ActivityManager(250): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityB u=0} from pid 10524 DEBUG/ActivityB(10524): onCreate() DEBUG/ActivityB(10524): onStart() INFO/ActivityManager(250): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityB: +225ms DEBUG/ActivityA(10524): onStop() ***  ActivityC *** INFO/ActivityManager(250): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityC u=0} from pid 10524 DEBUG/ActivityC(10524): onCreate() DEBUG/ActivityC(10524): onStart() INFO/ActivityManager(250): Displayed com.habrahabr.ActivityStackLifeCycle/.ActivityC: +204ms DEBUG/ActivityB(10524): onStop() ***  Home*** INFO/ActivityManager(250): START {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10200000 cmp=com.android.launcher/com.android.launcher2.Launcher u=0} from pid 250 DEBUG/ActivityC(10524): onStop() ***       *** INFO/ActivityManager(250): START {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityA u=0} from pid 476 DEBUG/ActivityA(10524): onNewIntent() DEBUG/ActivityA(10524): onStart() ***  ActivityC *** INFO/ActivityManager(250): START {cmp=com.habrahabr.ActivityStackLifeCycle/.ActivityC u=0} from pid 10524 DEBUG/ActivityC(10524): onNewIntent() DEBUG/ActivityC(10524): onStart() DEBUG/ActivityA(10524): onStop() ***  back *** DEBUG/ActivityA(10524): onStart() DEBUG/ActivityC(10524): onStop() DEBUG/ActivityC(10524): onDestroy()
      
      





Homeを押す前に、Task1 [A]、Task2 [B、C]という2つのエンティティがありました。 アプリケーションを再起動した後、ActivityAからActivityC、つまり さらに、 allowTaskReparenting



taskAffinity



いないallowTaskReparenting



taskAffinity



別のアプリケーションとして動作し、押し戻すとルートActivityBに戻ります。 オプションのおかげで、戻るボタンはTask1に戻りました。



実際には、このような複雑な作業スキームを1つのアプリケーション内に構築する必要はめったにありません。したがって、タスク1とタスク2のサイトに別々のアプリケーションを表示し、一方が他方のアクティビティを呼び出して短いタスクを実行し、押し戻してからデバイス画面を制御します。



android:alwaysRetainTaskState



デフォルトでは、ユーザーがアクセスしていない場合、システムは一定時間(「30分」など)©developer.android.comの後、アクティビティとともにタスクを破棄します。 ActivityパラメーターalwaysRetainTaskState



true



設定することにより、それらは永久に存続させることができます(メモリー不足の場合を除く)。 これは理論的に説明されており、ここでキャッチを想像するのは難しいので、テストは行いませんでした。

追加 :まだ問題があることが判明した。ara89コメントに感謝。 Android 4.0(APIレベル14)以降、アクティビティの破棄が停止し、このパラメーターは使用できなくなりました。 ここで2.3.7と4.0.1の差分を確認できますACTIVITY_INACTIVE_RESET_TIME



フィールドに注意してください)。 ただし、Androidトラッカーでこれに対してバグが作成されましたが、まだ新しいステータスであり、所有者はいません。



アクティビティスタックのライフサイクル(パート1)



All Articles