背景
Androidと外部デバイスをアプリケーション起動中はずっとコネクションしておいて、
アプリを終了したときに切断するということをしたかったので、アプリ終了イベントをバインドしたい。という背景。
Activityの生成や破棄の際には、適切なライフサイクル(onCreateやonPause)上で処理を書けばいいですが、
アプリ自体の初期化処理とか終了処理はどこに書けばいいのでしょうか。
色々調べたけどこれという情報には行きつかなくて、ここなのでは?という結論に達したのでまとめます。
Applicationクラスを継承する
1つめのやり方にApplicationクラスを使うやり方があります。
アプリケーションが起動する際に生成され、ライフサイクルを持つクラスです。
package tokyo.ysbrothersk.appcloseevent
import android.app.Application
class ApplicationManager : Application() {
override fun onCreate() {
super.onCreate()
// TODO: アプリ生成時処理
}
override fun onTerminate() {
super.onTerminate()
// TODO: アプリ終了時処理
}
}
OverrideしたonCreateに処理を書けばアプリ生成時に実行されます。
では、アプリ終了時の処理をonTerminateに書けばいいかというと、実はそうはいきません。
ここを見ればわかるのですが、
実はこのonTerminateはエミュレーターのときのみイベントが発火して、実機の時には呼び出されません。
つまり、実際のAndroid上でアプリが動いている場合は、このonTerminateは呼び出されずにアプリケーションが破棄されます。なんてこったい。
ではどうするかというと、ルートActivity上で管理することにしてみました。
ルートActivityで管理する
ルートActivityは、スタックの一番最下層にあるActivityのことで、つまり戻るキーを押したときに一番最後になるActivityです(一番最初に起動されるActivity)。
A → B → C の順にActivityを起動した場合、CとBでBackキーを押すとスタックからActivityが取り出されて行きますが、ルートActivityであるAでBackキーを押下するとアプリケーションが終了します。
このルートActivityが終了するときというのは、アプリケーションが終了するときなので、今回目的のアプリケーションの生存を持っているといえますね!
なので、このルートとなるActivityのライフサイクル上で色々処理を持たせておいてあげれば良さそうです。
package tokyo.ysbrothersk.appcloseevent
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import tokyo.ysbrothersk.appcloseevent.ui.main.MainFragment
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, MainFragment.newInstance())
.commitNow()
}
// TODO: アプリ開始処理
// TODO: Activity開始処理
}
override fun onStop() {
super.onStop()
// TODO: ユーザーデータなど消えては困るものはonPauseで処理を行う
}
override fun onDestroy() {
super.onDestroy()
// TODO: アプリ終了処理
}
}
デフォルトのまま、MainActivityという名前のルートActivityにonDestroyをオーバーライドしてあげて処理を書けば良さそうです。
単純に、このActivityが表示されているときに、BackキーとかタスクキルしてもonDestroyは呼ばれますし、
別のActivityが起動しているときに、Androidのタスクボタンからアプリを終了してもしっかり呼ばれます。素敵!
ただし、onDestroyを使う際は注意が必要です。
こちらに記載があるのですが、何かしらの原因でインスタンスメモリが改修された場合にこのonDestroyが呼ばれずにアプリが終了することもあるようです。
きちんと処理をしないとメモリリークを引き起こすようなものの処理は、onStop()で対処することがいいとあります。
また、コメントにもいただきましたが、
ユーザー情報など、保存しなくてはならないようなデータの場合はonStop()やonDestroy()ではなくデータが確定した段階で保存してあげるようなUX設計をする必要があります。
まとめ
今回は「アプリ終了時にデバイスのclose()処理を呼ぶ」という要件だったので、ルートActivityのonDestroy上で、openされていたらcloseするという実装にしました。
onDestroyが呼ばれずに終了した場合はcloseされないので良くはないのですが、とはいえonStop()で書くと画面遷移などでも呼ばれてしまうのでそういうわけにもいかず。。みたいな。
まあここらへんを想定するのはAndroidに限らず難しい話なような気がしますね。