Activityの連続起動を検知する
事の発端
自作のアプリケーションランチャーでHomeボタンの押下を検知したくなった。
Homeボタンを一回押したらランチャーを表示し、ランチャーが表示されている状態でHomeボタンを押したら別の外部のHomeアプリを起動したい(PreHomeみたいな動作)。
というわけでwebで調査していたところ、日本androidの会にActivity#onUserLeaveHintをオーバーライドすればHomeボタンの検知ができるとの情報があった。
しかしAPIリファレンスを読んでも、記述があいまいと言うか、いまいち要領を得ない。
試してみた
ActivityLifeCycleというテストアクティビティをつくってlogをトレースしてみる。
(1)Homeボタン押下(HomeApp起動)
ActivityLifeCycle(10487): onUserInteraction ActivityLifeCycle(10487): onUserLeaveHint ActivityLifeCycle(10487): onPause ActivityManager(120): Displayed activity com.example.android.home/.HomeApp: 285 ms (total 285 ms)
(2)Homeボタン長押し (アプリ履歴表示)
dalvikvm(204): GC_EXPLICIT freed 3053 objects / 178712 bytes in 79ms dalvikvm(120): GC_EXTERNAL_ALLOC freed 21870 objects / 973168 bytes in 105ms StatusBar(120): DISABLE_EXPAND: yes
- > 自Activity(ActivityLifeCycle )起動
ActivityManager(120): Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10100000 cmp=com.amle.android.mysample/.ActivityLifeCycle }
(3)Homeボタン長押し(アプリ履歴表示)
dalvikvm(120): GC_EXTERNAL_ALLOC freed 19860 objects / 988712 bytes in 104ms StatusBar(120): DISABLE_EXPAND: yes
-> 他アプリ(smartbar/.GridFavouristApps)起動
ActivityManager(120): Starting activity: Intent { flg=0x10100000 pkg=com.aps.smartbar cmp=com.aps.smartbar/.GridFavouristApps }
ActivityLifeCycle(10487): onUserInteraction
ActivityLifeCycle(10487): onUserLeaveHint
ActivityLifeCycle(10487): onPause
AudioFlinger(90): write blocked for 163 msecs, 2069 delayed writes, thread 0xdae0
ActivityManager(120): Displayed activity com.aps.smartbar/.GridFavouristApps: 421 ms (total 421 ms)
(4)検索ボタン長押し(他アプリ(smartbar/.GridFavouristApps)起動)
ActivityLifeCycle(10257): onUserInteraction ActivityLifeCycle(10257): onUserInteraction ActivityManager(120): Starting activity: Intent { act=android.intent.action.SEARCH_LONG_PRESS flg=0x10000000 cmp=com.aps.smartbar/.SearchButtonClickedReceiver } ActivityLifeCycle(10257): onUserInteraction ActivityLifeCycle(10257): onUserLeaveHint ActivityLifeCycle(10257): onPause ActivityLifeCycle(10257): onUserInteraction InputManagerService(120): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@445bd798 ActivityLifeCycle(10257): onUserInteraction ActivityManager(120): Starting activity: Intent { flg=0x10000000 pkg=com.aps.smartbar cmp=com.aps.smartbar/.GridFavouristApps }
(5)ステータスバーを開く
(反応なし)
- >他アプリ(MobileRTM/.Activities.RTMReminderActivity)起動
ActivityManager(120): Starting activity: Intent { flg=0x10000000 cmp=com.rememberthemilk.MobileRTM/.Activities.RTMReminderActivity bnds=[0,568][480,664] (has extras) }
ActivityLifeCycle(10487): onUserInteraction
ActivityLifeCycle(10487): onUserLeaveHint
ActivityLifeCycle(10487): onPause
AudioFlinger(90): write blocked for 174 msecs, 2072 delayed writes, thread 0xdae0
ActivityManager(120): Displayed activity com.rememberthemilk.MobileRTM/.Activities.RTMReminderActivity: 186 ms (total 186 ms)
(6)自Activityの表示中に外部のアラームアプリを使ってAlarmManager経由で他アプリ(myalarm/.TimeupActivity)を起動させる。
ActivityManager(52): Starting activity: Intent { act=android.intent.action.MAIN flg=0x10000000 cmp=com.amle.android.myalarm/.TimeupActivity (has extras) } ActivityLifeCycle(1183): onUserInteraction ActivityLifeCycle(1183): onUserLeaveHint ActivityLifeCycle(1183): onPause AlarmService(353): onStart
(7)自Activityで別スレッドを作り、10秒後にブラウザを起動。
ActivityManager(52): Starting activity: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.android.browser/.BrowserActivity } ActivityLifeCycle(1146): onUserInteraction ActivityLifeCycle(1146): onUserLeaveHint ActivityLifeCycle(1146): onPause
(8)自Activityで別スレッドを作り、10秒後に自Activityをfinish()する。
ActivityLifeCycle(1240): onPause
(9)自Activityで別スレッドを作り、10秒後に自アプリの別activity(MySample)を起動する。
dalvikvm(204): GC freed 117 objects / 5256 bytes in 66ms ActivityManager(52): Starting activity: Intent { cmp=com.amle.android.mysample/.MySample } ActivityLifeCycle(626): onUserInteraction ActivityLifeCycle(626): onUserLeaveHint ActivityLifeCycle(626): onPause
(10)自Activityで別スレッドを作り、10秒後に自アプリの別activity(MySample)を起動し、その後自分をfinish()する。
dalvikvm(544): GC freed 2963 objects / 314176 bytes in 87ms ActivityManager(52): Starting activity: Intent { cmp=com.amle.android.mysample/.MySample } ActivityLifeCycle(662): onUserInteraction ActivityLifeCycle(662): onUserLeaveHint ActivityLifeCycle(662): onPause
いろいろなパターンを試してみたが、まとめると
- 他のActivityを起動させるとonUserLeaveHint()が呼ばれる。(Homeキー、検索キー長押し、バックグラウンドスレッドからのstartActivity()やAlarmManagerからのブロードキャストなど手段を問わない。)
- 自アプリ内の他のactivityを起動させても呼ばれる。
- 自Activityを起動させても呼ばれない。
- 自Activityをfinish()させても呼ばれない。(ただし他のActivityを起動した後にfinish()してもonUserLeaveHint()は呼ばれる。)
- ステータスバーの開閉、標準のアプリ履歴の表示では呼ばれない。(そこから別のActivityを起動すると呼ばれる。)
…となる。
実際のところonUserLeaveHint()は、他activityが起動される時には、一部の例外(着呼のときには呼ばれない)を除いてほとんど常に呼ばれると思ったほうが良さそうだ。
結論:
onUserLeaveHint()は、他のactivityが起動された(startActivity()された)ことを検知するためのメソッドであると考えられる。
最初はonPause()との使い分けがわからなかったが、「電話の着信では呼ばれない」、「自activityをfinish()しても呼ばれない」という2点でonPause()と異なる。
結局のところHomeボタンの検知に使えるわけではなさそうだが2重起動の検知には使えそうだ。
つまり、onUserLeaveHint()を挟むことなく連続でonResume()が呼び出されたら、それは連続起動(ランチャーActivityの表示中にさらにランチャーActivityが呼び出された)である。
着呼の時にはonUserLeaveHint()が呼び出されないが、この時はActivityが終了するようなので連続起動扱いにはならない。
つまり、着呼の後には常にランチャーが表示されることになる。
この動作が正しいかどうかはアプリの仕様次第だろう。