iOSエンジニアのつぶやき

毎朝8:30に iOS 関連の技術について1つぶやいています。まれに釣りについてつぶやく可能性があります。

知識ゼロからの Kotlin Android アプリリリースへの軌跡 / Day13【Lifecycles and logging編】

学ぶこと

  • Log 情報を Logcat(Android console または Android monitor と呼ばれることもあります)に出力する方法。
  • ActivityFragment のライフサイクルの基本、および activity が状態間を移動する時に呼び出されるコールバック
  • ライフサイクルコールバックメソッドをオーバーライドして、Activity lifecycle の様々な時点で操作を実行する方法。
  • Timber ライブラリを使用してアプリにログインする方法。

すること

  • DessertClicker というスターターアプリを変更して、Logcat に表示されるログ情報を追加します。
  • ライフサイクルコールバックメソッドをオーバーライドし、Activity の変更をログに記録します。
  • アプリを実行し、Activity の開始、停止、再開時に表示されるログ情報を確認します。
  • Timber ライブラリを使用するようにアプリを変更します。
  • AndroidTrivia アプリにロギングを追加し、フラグメントの状態の変化を監視します。

アプリの概要

このコードラボでは、DessertClicker というスターターアプリを使用します。このアプリでは、ユーザが画面上のデザートをタップするたびに、アプリがユーザのためにデザートを購入します。アプリは、購入したデザートの数とユーザが費やした合計金額のレイアウトの値を更新します。

このアプリには、Android ライフサイクルに関連するいくつかのバグが含まれています。たとえば 、特定の状況では、アプリはデザートの値を0にリセットし、アプリがバックグランドにある時でもシステムリソースを使用し続けます。

ライフサイクルメソッドを探索し、基本的なロギングを追加する

全ての Activity と全ての Fragment には、ライフサイクルと呼ばれるものがあります。これは、この蝶のライフサイクルと同様に、動物のライフサイクルに対する暗示です。蝶の様々な状態は、誕生から完全に形成された成虫、死に至るまでの成長を示しています。

同様に、Activity のライフサイクルは、Activity が最初に初期化されてから最終的に破棄され、システムによってメモリが解放されるまで、Activity が通過する様々な状態で構成されます。ユーザがアプリを起動し、Activity 間を移動し、アプリの内外をナビゲートし、アプリを離れると、Activity の状態が変化します。以下の図は、Activity のライフサイクルの全ての状態を示しています。名前が示すように、これらの状態は Activity のステータスを表します。

多くの場合、動作を変更したり、Activity のライフサイクルの状態が変化した時にコードを実行したりします。したがって、Activity クラス自体、および AppCompatActivity などの Activity のサブクラスは、一連のライフサイクルコールバックメソッドを実装します。Android は、Activity がある状態から別の状態に移行した時にこれらのコールバックを呼び出します。独自の Activities でこれらのメソッドをオーバーライドして、ライフサイクルの状態の変化に応じてタスクを実行できます。次の図は、使用・オーバーライド可能なコールバックとともにライフサイクルの状態を示しています。

これらのコールバックがいつ呼び出され、各コールバックメソッドで何を実行するかを知ることが重要です。ただし、これらの図はどちらも複雑で、混乱を招く可能性があります。このコードラボでは、各状態とコールバックの意味を読み取るだけでなく、何が起こっているかを理解します。

onCreate() メソッドを調べてロギングを追加する

Android ライフサイクルで何が起こっているのかを理解するには、様々なライフサイクルメソッドがいつ呼び出されたかを知ることが役立ちます。これは、DessertClicker で問題が発生している場所を特定するのに役立ちます。

これを行う簡単な方法は、Android ロギング API を使用することです。ロギングを使用すると、アプリの実行中に短いメッセージをコンソールに書き込むことができ、それを使用して、様々なコールバックがトリガーされた時にそれらを表示できます。

  1. DesserClicker stater app をダウンロードして、AndroidStudio で開きます。

  2. アプリをコンパイルして実行し、デザートの写真を数回タップします。Desserts Sold の値と合計金額がどのように変化するかに注意してください。

  3. MainActivity.kt を開き、この Activity の onCreate() メソッドを調べます。

override fun onCreate(savedInstanceState: Bundle?) {
...
}

onCreate() はすべての Activity が実装する必要がある1つのメソッドです。onCreate() メソッドは、Activity の一回限りの初期化を行う場所です。たとえば、onCreate() では、レイアウトを Inflate したり、Click Listeners を定義したり、データバインディングを設定したりします。

onCreate() ライフサイクルメソッドは、Activity が初期化された直後(メモリ内に新しい Activity オブジェクトが作成された時)に一回呼び出されます。onCreate() の実行後、Activity は作成されたとみなされます。

Note:

onCreate() メソッドはオーバーライドであるため、その中ですぐに super.onCreate() を呼び出す必要があります。同じことが他のライフサイクルメソッドにも当てはまります。

  1. onCreate() メソッドで、super.onCreate() の呼び出しの直後に、次の行を追加します。必要に応じて Log クラスをインポートします。
Log.i("MainActivity", "onCreate Called")

Log クラスは、Logcat にメッセージを書き込みます。このコマンドには3つの部分があります。

  • severity ログメッセージ、つまりメッセージの重要度。Log.i()) メソッドは infomation message を書き込みます。Log クラスの他のメソッドには、エラーの場合は Log.e())、警告の場合は [Log.w(https://developer.android.com/reference/kotlin/android/util/Log#w(kotlin.String,%20kotlin.String))] などがあります。

  • ログ tag は、今回の場合だと "MainActivity" です。タグは、LogCat でログメッセージをより簡単に検索できる文字列です。タグは通常、クラスの名前です。

  • 実際のログメッセージは、今回の場合 "onCreate called" です。

  • DessertClicker アプリをコンパイルして実行します。デザートをタップしても、アプリの動作に違いはありません。 Android Studio の画面下部にある Logcat タブをクリックします。

Logcat は、メッセージをログに記録するためのコンソールです、アプリに関する Android からのメッセージがここに表示されます。これには、Log.i() メソッドまたは、他の Log クラスメソッドを使用して明示的にログに送信したメッセージが含まれます。

  1. Logcat ペインで、検索フィールドに I/MainActivity と入力します。

Logcat には多くのメッセージが含まれる場合があり、そのほとんどは役立ちません。Logcat エントリは様々な方法でフィルタリングできますが、検索が最も簡単です。MainActivity をコードのログタグとして使用していたため、そのタグを使用してログをフィルター処理できます。先頭に I/ を追加すると、information message を意味し、これは Log.i() のよって作成されます。

ログメッセージには、日付、時刻、パッケージの名前(com.example.android.dessertclicker)、ログタグ、および実際のメッセージが含まれています。このメッセージはログに表示されるため、onCreate() が実行されたことが分かります。

onStart() メソッドを実装する

onStart() ライフサイクルメソッドは、onCreate() の直後に呼び出されます。onStart() の実行後、Activity が画面に表示されます。Activity を初期化するために一回だけ呼び出される onCreate() とは異なり、onStart() は Activity のライフサイクルで何度でも呼び出すことができます。

onStart() は、対応する onStop() ライフサイクルメソッドとペアになっていることに注意してください。ユーザがアプリを起動してからデバイスのホーム画面に戻ると、Activity は停止し、画面に表示されなくなります。

  1. Android StudioMainActivity.kt を開いた状態で、Code > Override Methods を選択するか、Control + o を押します。このクラスでオーバーライドできる全てのメソッドのリストを含むダイアログが表示されます。

  1. onStart と入力して、適切なメソッドを検索します。リストから onStart() を選択し、OK をクリックしてボイラープレートオーバーライドコードを挿入します。コードは次のようになります。
override fun onStart() {
   super.onStart()
}

Tip: Android Studio は、オーバーライドされたメソッドコードを、クラス内の次に利用可能な適切な場所に挿入します。ライフサイクルオーバーライドを特定の場所(クラスの最後など)に配置する場合は、オーバーライドメソッドを使用する前に挿入ポイントを設定します。

  1. onStart() メソッド内に、ログメッセージを追加します:
override fun onStart() {
   super.onStart()

   Log.i("MainActivity", "onStart Called")
}
  1. DessertClicker アプリをコンパイルして実行し、Logcat ペインを開きます。ログをフィルタリングするには、検索フィールドに I/MainActivity と入力します。onCreate() メソッドと onStart() メソッドの両方が順番に呼び出され、Activity が画面に表示されていることに注意してください。

  2. バイスのホームボタンを押してから、最近の画面を使用して Activity に戻ります。Activity は中断したところから再開され、全ての同じ値で、onStart() が再度 Logcat に記録されます。通常、onCreate() メソッドが再度呼び出されないことにも注意してください。

Note: ライフサイクルのコールバックをデバイスで観察していると、デバイスを回転させた時に異常な動作が発生する場合があります。この動作については、次のコードラボで学習します。

ログに Timber を使用する

このタスクでは、Timber と呼ばれる一般的な logging library を使用するようにアプリを変更します。Timber には、組み込みの Android Log クラスに比べていくつかの利点があります。特に、Timber ライブラリ:

  • クラス名に基づいて log tag を生成します。

  • Android アプリのリリースバージョンでログが表示されないようにします。

  • crash-reporting ライブラリとの統合を可能にします。

Gradle に Timber を追加する

  1. GithubTimber project にアクセスし、implementation という単語で始まる Download 見出しの下のコード行をコピーしてください。コードの行は次のようになりますが、バージョン番号は異なる場合があります。
implementation 'com.jakewharton.timber:timber:4.7.1'
  1. Gradle Scripts を開いて、build.gradle(Module:app) ファイルを開きます。

  2. dependencies セクション内に、コピーしたコード行を貼り付けます。

dependencies {
   ...
   implementation 'com.jakewharton.timber:timber:4.7.1'
}
  1. Android Studio の右上にある Sync Now をクリックして、Gradle を再構築します。ビルドはエラーなしで実行されます。

Application クラスを作成して Timber を初期化する

このステップでは、アプリケーションクラスを作成します。アプリケーションは、アプリケーション全体のグローバルアプリケーション状態を含む基本クラスです。また、オペレーティングシステムがアプリとの対話に使用する主要なオブジェクトでもあります。指定しない場合、Android が使用するデフォルトの Application クラスがあるため、特別なことを行わなくても、アプリケーション用に作成された Application オブジェクトが常に存在します。

TimberApplication クラスを使用します。これは、アプリ全体がこのロギングライブラリを使用するため、他の全てを設定する前に、ライブラリを一同初期化する必要があるためです。このような場合、Application クラスをサブクラス化して、独自のカスタム実装でデフォルトをオーバーライドできます。

Warning: クラスは全ての Activity の前に作成され、グローバルな状態を保持できるため、Application クラスに独自のコードを追加するのは魅力的です。しかし、グローバルに利用できる読み取りおよび書き込み可能な静的変数を作成するのがエラーが発生しやすいのと同様に、Application クラスを悪用するのは簡単です。コードが本当に必要でない限り、Application クラスに Activity コードを配置しないでください。

Application クラスを作成したら、Android マニフェストでクラスを指定する必要があります。

  1. dessertclicker パッケージで、ClickerApplication という新しい Kotlin クラスを作成します。これを行うには、app > java を展開し、com.example.android.dessertclicker を右クリックします。New > Kotlin File/Class を選択します。

  2. クラスに ClickerApplication という名前をつけ、Kind Class に設定します。OK をクリックします。

Android Studio は新しい ClickerApplication クラスを作成し、コードエディターで開きます。コードは次のようになります。

package com.example.android.dessertclicker

class ClickerApplication {
}
  1. クラス定義を Application のサブラクスに変更し、必要に応じて Application クラスをインポートします。
class ClickerApplication : Application() { }
  1. onCreate() メソッドをオーバーライドするには、Code > Override Methods を選択するか、Control + o を押します。
class ClickerApplication : Application() {
   override fun onCreate() {
       super.onCreate()
   }
}
  1. onCreate() メソッド内で、Timber ライブラリを初期化します。
override fun onCreate() {
    super.onCreate()

    Timber.plant(Timber.DebugTree())
}

このコード行は、アプリの Timber ライブラリを初期化して、Activity でライブラリを使用できるようにします。

  1. AndroidManifest.xml を開きます。

  2. <application> 要素の上に ClickerApplication クラスの新しい attributes を追加します。これにより、Android がデフォルトの Application クラスではなく、ClickerApplication クラスを使用することが認識されます。

<application
   android:name=".ClickerApplication"
...

Note: Application クラスを Android マニフェストに追加しない場合、アプリはエラーなしで実行されます。ただし、アプリは作成したクラスを使用しないため、Timber からのログ情報は表示されません。

Timber log ステートメントを追加する

このステップでは、Timber を使用するように Log.i() 呼び出しを変更してから、他の全てのライフサイクルメソッドのロギングを実装します。

  1. MainActivity を開き、onCreate() までスクロールします。Log.i()Timber.i() に置き換え、ログタグを削除します。
Timber.i("onCreate called")

Log クラスと同様に、Timber も informational messages に i() メソッドを使用します。Timber を使用すると、ログタグを追加する必要がないことに注意してください。Timber は自動的にクラスの名前をログタグとして使用します。

  1. 同様に、onStart()Log 呼び出しを変更します。
override fun onStart() {
   super.onStart()

   Timber.i("onStart Called")
}
  1. DessertClicker アプリをコンパイルして実行し、Logcat を開きます。onCreate()onStart() で同じログメッセージが表示されていることに注目してください。

  2. MainActivity の残りのライフサイクルメソッドをオーバーライドし、それぞれに Timber ログステートメントを追加します。下記がコードになります。

override fun onResume() {
   super.onResume()
   Timber.i("onResume Called")
}

override fun onPause() {
   super.onPause()
   Timber.i("onPause Called")
}

override fun onStop() {
   super.onStop()
   Timber.i("onStop Called")
}

override fun onDestroy() {
   super.onDestroy()
   Timber.i("onDestroy Called")
}

override fun onRestart() {
   super.onRestart()
   Timber.i("onRestart Called")
}
  1. DessertClicker をコンパイルして再度実行し、Logcat を調べます。今回は、onCreate()onStart() に加えて、onResume() ライフサイクコールバックのログメッセージがあることに注意してください。

Activity が最初から開始すると、次の3つのライフサイクルコールバック全てが順番に呼び出されることが分かります:

  • onCreate() アプリを作成
  • onStart() 起動して画面に表示
  • onResume() Activity がフォーカスを与え、ユーザが操作できるようになる

onResume() メソッドは、再開するものがない場合でも、起動時に呼び出されます。

ライフサイクルのユースケースを調べる

DessertClicker アプリがロギング用に設定されたので、さまざまな方法でアプリの使用を開始する準備が整い、これらの用途に応じてライフサイクルコールバックがどのようにトリガーされるかを調べる準備ができました。

Activity の開閉

最も基本的なユースケースから始めます。つまり、アプリを初めて起動し、アプリを完全に閉じます。

  1. DessertClicker アプリがまだ実行されていない場合は、コンパイルして実行します。ご覧のとおり、onCreate()onStart()onResume() コールバックは、Activity が初めて開始される時に呼び出されます。

  1. カップケーキを数回タップします。

  2. バイスの Back button をタップします。Logcat で、onPause()onStop()onDestroy() の順序で呼ばれることに注目してください。

この場合、Back button を使用すると、Activity が完全に閉じます。onDestroy() メソッドの実行は、Activity が完全にシャットダウンされ、ガベージコレクションできることを意味します。Garbage collection) とは、今後使用しないオブジェクトの自動クリーンアップを指します。onDestroy() が呼びだされた後、OS はそれらのリソースが破棄可能であることを認識し、そのメモリのクリーンアップを開始します。

コードが手動で Activity の finish()) メソッドを呼び出した場合、またはユーザーがアプリを強制終了した場合も、Activity が完全にシャットダウンされることがあります。また、アプリが画面に長時間表示されていない場合、Android システムが自動的に Activity をシャットダウンする場合もあります。Android はこれをすることで、バッテリーを節約し、アプリのリソースを他のアプリで使用できるようにします。

  1. 最近の画面を使用してアプリに戻ります。Logcat は次のとりおりです:

Activity は前のステップで破棄されたため、アプリに戻ると、Android は新しい Activity を開始し、onCreate()onStart()onResume() メソッドを呼び出します。前の Activity の DessertClicker の統計が保持されていないことに注意してください。

ここで重要なのは、onCreate()onDestroy() が単一の Activity インスタンスの有効期間中に一度だけ呼び出されるということです。onCreate() はアプリを初めて初期化し、onDestroy() はアプリが使用するリソースをクリーンアップします。

onCreate() メソッドは重要なステップです。これは、最初に全ての初期化が行われる場所・最初にレイアウトをインフレートして設定する場所・および変数を初期化する場所です。

Activity から離れて Activity に戻る

アプリを起動して完全に閉じたので、Activity が初めて作成された時のほとんどのライフサイクル状態を確認しました。また、Activity が完全にシャットダウンして破壊された時に、Activity が通過する全てのライフサイクル状態を確認しました。しかし、ユーザは Android 搭載デバイスを操作する時に、アプリを切り替えたり、Home に戻ったり、新しいアプリを起動したり、電話などの他の Activity による割り込みを処理したりします。

ユーザがその Activity から離れるたびに、Activity が完全に終了するわけではありません:

  • Activity が画面に表示されなくなった場合、これは Activity を background に配置すると呼ばれます。(この反対は、Activity が foreground または画面上にある場合です。)
  • ユーザがアプリに戻ると、同じ Activity が再開され、再び表示されます。ライフサイクルのこの部分は、visible lifecycle と呼ばれます。

アプリが background にある時は、システムリソースとバッテリー寿命を維持するために、アプリを Active に実行しないでください。Activity のライフサイクルとそのコールバックを使用して、アプリがバックグランドに移行するタイミングを把握し、進行中の操作を一時停止できるようにします。次にアプリが foreground になった時に操作を再開します。

例えば、物理シュミレーションを実行するアプリを考えてみましょう。シュミレーションないの全てのオブジェクトを配置する場所を決定し、それを表示するには、デバイスの CPU でクランチされた多くの計算が必要です。

これにはパフォーマンス上の理由もあります。ユーザが、CPU 集中型の物理シュミレーションを使用する20個のアプリを開いたとしましょう。それらのアプリの Activity が画面上にない場合でも、background で大量のレンダリング計算を実行していると、Device 全体のパフォーマンスが低下します。

このステップでは、アプリが background に入り、再び foreground に戻る時の Activity のライフサイクルを確認します。

  1. DessertClicker アプリが実行されている状態で、カップケーキを数回クリックします。

  2. バイスのホームボタンを押して、Android Studio の Logcat を観察します。ホーム画面に戻ると、アプリが完全にシャットダウンされるのではなく、アプリがバッググランドになります。onPause() メソッドと onStop() メソッドは呼び出されますが、onDestroy() は呼び出されないことに注意してください。

onPause() が呼び出されると、アプリはフォーカスを失います。onStop() の後、アプリは画面に表示されなくなります。Activity は停止していますが、Activity オブジェクトはまだバックグランドでメモリ内にあります。Activity は破棄されていません。ユーザがアプリに戻る可能性があるため、Android は Activitu Resources を保持します。

  1. 最近の画面を使用してアプリに戻ります。Logcat で、Activity が onRestart() および onStart() で再開され、onResume() で再開されることに注目してください。

Activity が Foreground に戻ると、onCreate() メソッドは呼び出されません。Activity オブジェクトは破棄されなかったため、再度作成する必要はありません。onCreate() の代わりに、onRestart() が呼び出されます。今回は、Activity が Foreground に戻った時に、デザートの販売数が保持されていることに注目してください。

  1. DessertClicker 以外のアプリを少なくとも1つ起動して、デバイスの最近の画面にいくつかのアプリがあるようにします。

  2. 最近の画面を表示して、別の最近の Activity を開きます。次に、最近のアプリに戻り、DessertClicker を Foreground に戻します。

ここでは、ホームボタンを押した時に同じコールバックが Logcat に表示されます。onPause()onStop() は、アプリがバックグランドになると呼び出され、onRestart()onStart()onResume() が呼び出されます。

ここでの重要な点は、ユーザが Activity に出入する時に onStart() および、onStop() が複数回呼び出されることです。これらのメソッドをオーバーライドして、アプリがバックグランドに移動した時に停止するか、Foreground に戻った時に再起動する必要があります。

では、onRestart() はどうでしょうか? onRestart() メソッドは onCreate() によく似ています。Activity が表示される前に onCreate() または onRestart() が呼び出されます。onCreate() は最初にのみ呼び出され、その後 onRestart() が呼び出されます。onRestart() メソッドは、Activity が初めて開始されていない場合にのみ呼び出すコードを配置する場所です。

Activity を部分的に隠す

アプリを起動して onStart() を呼び出すと、アプリが画面に表示され、onResume() が呼び出されるようになることを学びました。アプリが再開され、onResume() が呼び出されると、アプリはユーザフォーカスを取得します。アプリが完全に画面上にあり、ユーザフォーカスがあるライフサイクルの部分は、interactive lifecycle と呼ばれます。

アプリがバックグランドになると、onPause() の後で、フォーカスが失われ、onStop() の後でアプリが表示されなくなります。

フォーカスと可視性の違いは重要です。Activity が画面上に部分的に表示されていても、ユーザフォーカスがない場合があるためです。このステップでは、Activity が部分的に表示されているが、ユーザにフォーカスがない1つのケースを調べます。

  1. DessertClicker アプリが実行されている状態で、画面の右上にある Share ボタンをクリックします。

Share Activity は画面の下半分に表示されますが、Activity はまだ上半分に表示されています。

  1. Logcat を調べて、onPause() のみが呼び出されたことに注目してください。

このユースケースでは、Activity がまだ部分的に表示されているため、onStop() は呼び出されません。ただし、Activity にはユーザフォーカスがなく、ユーザは Activity を操作できません。Foreground にある "share" activity は、ユーザフォーカスを保持しています。

この違いが重要なのはなぜでしょう?物理アプリを考えてみましょう。アプリがバックグランドにある時にシュミレーションを停止し、アプリが部分的に隠されている時に実行を継続したい場合があります。この場合、onStop() でシュミレーションが停止します。Activity が部分的に隠されている時にシュミレーションを停止する場合は、シュミレーションを停止するコードを onPause() に配置します。

onPause() で実行されるコードは全て、他のものの表示をブロックするため、pnPause() のコードは軽量にする必要があります。たとえば、電話がかかってきた場合、onPause() のコードによって着信通知が遅れる可能性があります。

  1. share dialog の外側をクリックしてアプリに戻り、onResume() が呼び出されていることを確認します。

onResume()onPause() はどちらもフォーカスと関係があります。onResume() メソッドは、Activity にフォーカスがある時に呼び出され、onPause() は、Activity がフォーカスを失った時に呼び出されます。

Fragment ライフサイクルをを調べる

Android fragment のライフサイクルは、Activity のライフサイクルと似ていますが、Fragment 固有の方法がいくつかあります。

このタスクでは、以前のコードラボで構築した AndroidTrivia アプリを確認し、ログを追加して Fragment のライフサイクルを調査します。

AndroidTrivia アプリの各画面は Fragment です。

シンプルに調査するために、このタスクでは Timber ライブラリではなく Android ロギング API を使用します。

  1. AndroidTrivia アプリを開きます。

  2. TitleFragment.kt ファイルを開きます。AndroidStudio では、アプリを rebuild するまで、バインドエラーと未解決の参照エラーが表示される場合があることに注意してください。

  3. onCreateView() メソッドまでスクロールします。ここで Fragment のレイアウトが inflate され、data binding が発生することに注意してください。

  4. setHasOptionsMenu() の行と最後の呼び出しの間に、onCreateView() メソッドにロギングステートメントを追加します。

setHasOptionsMenu(true)

Log.i("TitleFragment", "onCreateView called")

return binding.root
  1. onCreateView() メソッドの下に、残りの Fragment ライフサイクルメソッドごとにロギングステートメントを追加します。下記がコードになります:
override fun onAttach(context: Context) {
   super.onAttach(context)
   Log.i("TitleFragment", "onAttach called")
}
override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   Log.i("TitleFragment", "onCreate called")
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    Log.i("TitleFragment", "onViewCreated called")
}

override fun onStart() {
   super.onStart()
   Log.i("TitleFragment", "onStart called")
}
override fun onResume() {
   super.onResume()
   Log.i("TitleFragment", "onResume called")
}
override fun onPause() {
   super.onPause()
   Log.i("TitleFragment", "onPause called")
}
override fun onStop() {
   super.onStop()
   Log.i("TitleFragment", "onStop called")
}
override fun onDestroyView() {
   super.onDestroyView()
   Log.i("TitleFragment", "onDestroyView called")
}
override fun onDetach() {
   super.onDetach()
   Log.i("TitleFragment", "onDetach called")
}
  1. アプリをコンパイルして実行し、Logcat を開きます。

  2. 検索フィールドに I/TitleFragment と入力して、ログをフィルタリングします。アプリが起動すると、Logcat は次のようになります。

21933-21933/com.example.android.navigation I/TitleFragment: onAttach called
21933-21933/com.example.android.navigation I/TitleFragment: onCreate called
21933-21933/com.example.android.navigation I/TitleFragment: onCreateView called
21933-21933/com.example.android.navigation I/TitleFragment: onViewCreated called
21933-21933/com.example.android.navigation I/TitleFragment: onStart called
21933-21933/com.example.android.navigation I/TitleFragment: onResume called

ここでは、これらのコールバックを含む、Fragment の起動ライフサイクル全体を確認できます。

  • onAttach(): Fragment がその所有者 Activity に関連付け羅れている時に呼び出されます。
  • onCreate(): Activity の onCreate() と同様に、(レイアウト以外の)最初の Fragment 作成を行うために呼び出されます。
  • onCreateView(): Fragment のレイアウトを Inflate するために呼び出されます。
  • onViewCreated(): onCreateView() が戻った直後に呼び出されます。ただし、保存された状態が view に復元される前に呼び出されます。
  • onStart(): Activity の onStart() と並行し、Fragment が表示された時に呼び出されます。
  • onResume(): Activity の onResume() と並行して、Fragment がユーザフォーカスを取得した時に呼び出されます。

  • Play ボタンをタップしてクイズゲームに進み、Logcat に注目してください。

21933-21933/com.example.android.navigation I/TitleFragment: onAttach called
21933-21933/com.example.android.navigation I/TitleFragment: onCreate called
21933-21933/com.example.android.navigation I/TitleFragment: onCreateView called
21933-21933/com.example.android.navigation I/TitleFragment: onViewCreated called
21933-21933/com.example.android.navigation I/TitleFragment: onStart called
21933-21933/com.example.android.navigation I/TitleFragment: onResume called
21933-21933/com.example.android.navigation I/TitleFragment: onPause called
21933-21933/com.example.android.navigation I/TitleFragment: onStop called
21933-21933/com.example.android.navigation I/TitleFragment: onDestroyView called

次の Fragment を開くと、タイトル Fragment が閉じ、次のライフサイクルメソッドが呼び出されます:

  • onPause(): フラグメントがユーザフォーカスを失った時に呼び出されます。Activity の onPause() と並行しています。

  • onStop(): Fragment が画面に表示されなくなった時に呼び出されます。Activity の onStop() と並行しています。

  • onDestroyView(): Fragment の View が不要になった時に呼び出され、その View に関連づけられているリソースをクリーンアップします。

  • アプリで、画面左上の Up button をタップして、Title Fragment に戻ります。

21933-21933/com.example.android.navigation I/TitleFragment: onPause called
21933-21933/com.example.android.navigation I/TitleFragment: onStop called
21933-21933/com.example.android.navigation I/TitleFragment: onDestroyView called
21933-21933/com.example.android.navigation I/TitleFragment: onCreateView called
21933-21933/com.example.android.navigation I/TitleFragment: onViewCreated called
21933-21933/com.example.android.navigation I/TitleFragment: onStart called
21933-21933/com.example.android.navigation I/TitleFragment: onResume called

今回は、Fragment を開始するための onAttach()onCreate() が呼び出されない可能性があります。Fragment オブジェクトはまだ存在し、その所有者 Activity にアタッチされているため、ライフサイクルは onCreateView() で再開されます。

  1. バイスのホームボタンを押します。Logcat で、onPause()onStop() のみが呼び出されることに注意してください。これは Activity の場合と同じ動作です。ホームに戻ると、Activity と Fragment が Background に配置されます。

  2. 最近の画面を使用してアプリに戻ります。Activity で発生したのと同じように、onStart()onResume() メソッドが呼び出され、Fragment が Foreground に返されます。

まとめ

Activity lifecycle

  • Activity lifecycle は、Activity が移行する一連の状態です。Activity のライフサイクルは、Activity が最初に作成された時に始まり、Activity が破棄された時に終了します。

  • ユーザが Activity 間やアプリの内部と外部を移動すると、各 Activity は Activity lifecycle の状態間を移動します。

  • Activity lifecycle の各状態には、 Activity のクラスでオーバーライドできるコールバックメソッドがあります。7つのライフサイクル methods があります。 onCreate()) onStart()) onPause()) onRestart()) onResume()) onStop()) onDestroy())

  • Activity が lifecycle state に移行する時に発生する動作を追加するには、state's のコールバックメソッドをオーバーライドします。

  • Android Studio のクラスにスケルトンオーバーライドメソッドを追加するには、Code > Override Methods を選択するか、Control + o を押します。

Log を使用してロギング

  • Android logging API、特に Log クラスを使用すると、AndroidStudio 内の Logcat に表示されるショートメッセージを作成できます。
  • Log.i() を使用して、information message を記述します。このメソッドは2つの引数をとります。ログタグとログメッセージです。
  • Android StudioLogcat ペインを使用して、書き込んだメッセージを含むシステムログを表示します。

Timber を使用してロギング

Timber は、Android ロギング API に比べていくつかの利点があるロギングライブラリです。特に、Timber ライブラリ:

  • クラス名に基づいてログタグを生成します。
  • Android アプリのリリースバージョンでログが表示されないようにするのに役立ちます。
  • クラッシュレポートライブラリとの統合を可能にします。

Timber を使用するには、その依存関係を Gradle ファイルに追加し、Application クラスを拡張して初期化します。

  • Application は、アプリ全体のグローバルアプリケーション状態を含む基本クラスです。指定しない場合に、Android で使用されるデフォルトの Application クラスがあります。独自の Application サブクラスを作成して、Timber などのアプリ全体のライブラリを初期化できます。

  • Android manifest の <application> 要素に android:name attribute を追加して、カスタム Application クラスをアプリに追加します。これを行うことを忘れないでください。

  • Timber.i() を使用して、Timber でログメッセージを書き込みます。このメソッドは、書き込むメッセージという1つの引数のみとります。ログタグは自動的にクラスの名前が割り当てられます。

その他の記事

yamato8010.hatenablog.com

yamato8010.hatenablog.com

yamato8010.hatenablog.com