Android Kotlin基礎講座 04.1: ライフサイクルとログ情報について
目次
タスク:ライフサイクルのユースケースについて知る
現在DessertClickerアプリはロギング用にセットアップされました。アプリを様々な方法て使う準備ができました。ここからはライフサイクルコールバックがどのように呼び出されているのかを探索していきます。
ユースケース1:アクティビティのオープンとクローズ
最も一般的なユースケースから始めていきましょう。アプリを最初に起動したときとアプリを完全に終了した場合です。
- まだアプリが開いていない場合はコンパイルしてアプリを起動してください。先ほど確認したように、onCreate()、onStart()、onResume()の三つのコールバックが最初に呼び出されています。
- カップケーキを何回かタップしてください。
- 端末の戻るボタンをタップしてください。LogcatでonPause()、onStop()、onDestroy()が順番に呼び出されたことを確認してください。
今回の場合は戻るボタンをタップすることでアクティビティ(アプリ)が完全に閉じられます。onDestroy()メソッドの実行はアクティビティが完全に終了し、Garbageコレクションのgarbageとして扱われることを意味します。Garbageコレクション(英語)にもう使わないオブジェクトの自動クリーンアップについて紹介されています。onDestroy()が呼び出された後、OSはそれらのリソースが廃棄可能であると判断し、メモリーのクリーンアップを行います。
アクティビティはコードでfinish()メソッドを呼び出した場合やユーザーが強制終了した場合も完全に閉じられることがあります。(例として、ユーザーは最近開いた画面からアプリを強制終了することができます)
またAndroidシステムはアプリが長時間画面上にアプリが表示されていない場合もアクティビティを閉じることがあります。これはバッテリーをキープしたり、他のアプリに割けるリソースを確保するためでもあります。 - 最近開いた画面からアプリに戻ってください。Logcatは以下のようになります。
アクティビティは前のステップで破棄されているので、アプリに戻るとAndroidは新しいアクティビティをスタートアップさせ、onCreate()とonStart()、onResume()を呼び出します。前のアクティビティからのアプリの状態(デザートの数や値段)が保持されていないことを確認してください。
ここでのキーポイントはonCreate()とonDestroy()が一つのアクティビティインスタンスのライフタイムの中で一度しか呼ばれていないということです。onCreate()はアプリの初期化のために一番最初に、onDestroy()はアプリで使われたリソースのクリーンアップのために呼び出されています。
onCreate()メソッドは重要なステップです。というのもonCreate()ですべての初期化が行われるからです。インフレートによるレイアウトのセットアップや変数の初期化等です。
ユースケース2:アクティビティから離れた後に戻る
先ほどはアプリをスタートしたあとに完全にアプリを閉じ、アクティビティが最初に作られる際のライフサイクル状態のほとんどを目にしました。またアクティビティが完全に閉じられ、破棄された際のライフサイクル状態も全て見ています。
しかしユーザーはAndroid端末を操作する際、アプリ、ホーム画面、他のアプリなどを切り替えたり、着信などのアクティビティによってアプリを中断したりします。
アクティビティはユーザーがそのアクティビティから離れる際に毎回完全に閉じるわけではありません。
- アクティビティが画面から全く見えない状態であるとき、アクティビティはバックグラウンドにあると表現されます。(これの反対はアクティビティがフォアグラウンドにあると言います)
- ユーザーがアプリに戻るとき、同じアクティビティが再開され、再びユーザーの目に見えるようになります。ライフサイクルのこの部分をアプリの可視ライフサイクルと言います。
アプリがバックグラウンドにあるとき、システムリソースやバッテリー寿命を守るためにもアプリが活発であるべきではありません。アプリがいつバックグラウンドに移行するのかをActivityライフサイクルとコールバックを使って知り、実行中の処理を停止させることができます。アプリがフォアグラウンドに戻ってきたとき、その処理を再開させます。
例として、物理シミュレーションをするアプリについて考えてみましょう。シミュレーション中のオブジェクトの位置を決定し、描写するためには多くの計算を必要とし、端末のCPUを消耗します。もし着信がシミュレーションを中断させ、ユーザーがアプリに戻ったときにはシミュレーションが終わってしまっていたら、ユーザーは困惑したり不満に思うでしょう。
これにはパフォーマンス上の理由もあります。CPUを消耗する物理シミュレーションアプリを20個開いていたとしましょう。もしそれらのアプリのアクティビティが画面上には表示されていない状態で重い計算をバックグラウンドで行っていた場合、端末全体の動作をスローダウンさせることになるでしょう。
このステップではアプリがバックグラウンドに行き、その後フォアグラウンドに戻ってくるときのアクティビティのライフサイクルを確認します。
- DessertClickerアプリが起動している状態でカップケーキを何回かタップしてください。
- 端末のホームボタンを押し、Logcatを確認してください。ホーム画面に戻るということはアプリは完全に閉じられるのではなく、バックグラウンドにおかれるということです。onPause()メソッドとonStop()メソッドが呼び出され、onDestroy()メソッドは呼び出されていないことを確認してください。
onPause()が呼び出されたとき、アプリにはフォーカスが当たっていません。onStop()の後、アプリが画面上から見えなくなります。アクティビティは停止していますが、Activityオブジェクトはまだメモリ上に残っており、バックグラウンドにあります。アクティビティは破棄されていないということです。ユーザーがアプリに戻るかもしれないので、Androidはアクティビティをキープしているのです。 - 最近開いた画面からアプリに戻ってください。Logcatを見て、アクティビティがonRestart()とonStart()によって再起動し、onResume()によって再開されているのを確認してください。
アクティビティがフォアグラウンドに戻るとき、onCreate()メソッドは呼び出されていません。アクティビティオブジェクトが破棄されていないので、再び作成する必要がないからです。
onCreate()の代わりにonRestart()が呼び出されています。アクティビティがフォアグラウンドに戻った際はDesserts Soldの数値が保持されていることを確認してください。 - DessertClicker以外のアプリを最低一つ開いて、端末の最近開いた画面にいくつかアプリが表示されるようにしてください。
- 最近開いた画面を遡って別のアクティビティを開いてください。その後DessertClickerに戻ってください。
ホームボタンを押したときと同じコールバックがLogcatに表示されていることを確認してください。onPause()とonStop()がアプリがバックグラウンドにいく際に呼び出され、onRestart()、onStart()、そしてonResume()が戻ってくる際に呼び出されています。
ここで重要な点は、ユーザーがアクティビティを遷移するときにonStart()とonStop()が複数回呼び出されているということです。アプリがバックグラウンドにに行くときにアプリを停止させるため、またフォアグラウンドに戻ってきたときにアプリを再開させるために、これらのメソッドをオーバーライドするべきです。
onRestart()についてはどうなのかというと、onRestart()はonCreate()によく似ています。onCreate()もonRestart()もアクティビティが表示される前に呼び出されます。onCreate()メソッドは最初の一回しか呼び出されません。onRestart()はその後に呼び出されます。onRestart()メソッドはアクティビティが初回に起動する場合でないときに行いたい処理を記述する場所です。
ユースケース3:部分的にアクティビティを隠す
アプリが起動しonStart()が呼び出されるとアプリが画面上で見えるようになるということを学習しました。アプリが再開され、onResume()が呼び出されるとアプリはユーザーフォーカスを取得します。アプリが完全に画面上にあり、ユーザーフォーカスを得ているライフサイクルのこの部分のことをインタラクティブ(対話的)ライフサイクルと呼びます。
アプリがバックグラウンドにいくとき、onPause()のあとにフォーカスが失われ、onStop()のあとにはアプリが画面に表示されなくなります。
フォーカスと可視性の違いは重要です。なぜならアクティビティが武運的に画面上に見えているが、ユーザーフォーカスを持っていない場合というのがあり得るからです。このステップではアクティビティが部分的に見え、ユーザーフォーカスを持っていないケースについて見ていきます。
- DessertClickerを立ちあげた状態でシェアボタンをタップしてください。
画面の下半分にシェア用のアクティビティが現れます。しかしアプリのアクティビティも画面の上半分に見ることができます。 - Logcatを見て、onPause()が呼び出されていることを確認してください。
このケースではonStop()は呼び出されていません。なぜならアクティビティはまだ部分的に見えているからです。しかしアクティビティはユーザーフォーカスは持っていません。そしてユーザーはアプリとやり取りすることができません。フォアグラウンドにある”共有”アクティビティがユーザーフォーカスを持っているからです。
どうしてこの違いか重要なのか、物理演算アプリについて考えてみましょう。アプリがバックグラウンドにあるときはシミュレーションを停止させ、アプリが部分的にでも見えている場合は動作させ続けたいとします。この場合、onStop()の中でシミュレーションを停止させれば良いわけです。もしアクティビティが部分的に見えている場合でもシミュレーションを停止したい場合、シミュレーションを停止するコードをonPause()の中に置きます。
onPause()中で実行されるコードは全て他の物が表示されないようにブロックしてしまうので、onPause()の中のコードは軽量にしてください。例として、着信があった場合、onPause()の中のコードは着信通知を遅らせる可能性があります。 - シェアダイアログの外側とタップしてアプリに戻ってください。onResume()が呼び出されたことを確認してください。
onResume()とonPause()は両方ともフォーカスと関係しています。onResume()メソッドはアクティビティがフォーカスを持っているときに呼び出され、onPause()はアクティビティがフォーカスを失うときに呼び出されます。