LoginSignup
0
1

More than 3 years have passed since last update.

【和訳】Lifecycle-Aware Components(Codelabs for the 2019 Android Dev Summit: Mountain View, October 23-24, 2019.)

Last updated at Posted at 2019-12-06

Android Dev Summit 2019
Codelabs for the 2019 Android Dev Summit: Mountain View, October 23-24, 2019.

Lifecycle-Aware Componentsの日本語訳

1.はじめに

構成部品

アーキテクチャコンポーネントは、堅牢、テスト可能、保守可能な方法でアプリを構築するのに役立つAndroidライブラリのセットです。
このコードラボでは、Androidアプリを構築するための次のライフサイクル対応アーキテクチャコンポーネントを紹介します。

  • ViewModel-特定のライフサイクルにバインドされたオブジェクトを作成および取得する方法を提供します。ViewModelは通常、ビューのデータの状態を保存し、データリポジトリやビジネスロジックを処理するドメインレイヤーなどの他のコンポーネントと通信します。このトピックの入門ガイドを読むには、ViewModelを参照してください。
  • LifecycleOwnerLifecycleOwnerは、AppCompatActivityおよびFragmentクラスによって実装されるインターフェイスです。このインターフェイスを実装する所有者オブジェクトに他のコンポーネントをサブスクライブして、所有者のライフサイクルの変更を観察できます。このトピックの入門ガイドを読むには、ライフサイクルの処理を参照してください。
  • LiveData-明示的で厳密な依存関係のパスを作成することなく、アプリの複数のコンポーネントにわたるデータの変更を観察できます。LiveDataは、アクティビティ、フラグメント、サービス、またはアプリで定義されたLifecycleOwnerなど、アプリコンポーネントの複雑なライフサイクルを尊重します。LiveDataは、停止したLifecycleOwnerオブジェクトへのサブスクリプションを一時停止し、終了したLifecycleOwnerオブジェクトへのサブスクリプションをキャンセルすることにより、オブザーバーサブスクリプションを管理します。このトピックの入門ガイドを読むには、LiveDataを参照してください。

構築するもの

このコードラボでは、上記の各コンポーネントの例を実装します。サンプルアプリから始めて、一連の手順でコードを追加し、進行に応じてさまざまなアーキテクチャコンポーネントを統合します。

必要なもの

  • Android Studio 3.5以降
  • Androidアクティビティのライフサイクルに精通している

2.ステップ1-環境のセットアップ

この手順では、コードラボ全体のコードをダウンロードしてから、簡単なサンプルアプリを実行します。

$ git clone git@github.com:googlecodelabs/android-lifecycles.git

または、リポジトリをZipファイルとしてダウンロードできます。

ソースコードをダウンロード

コードを入手したら

  1. プロジェクトAndroid Studioバージョン2.5以降を開きます。
  2. コードルートディレクトリから、このコードラボで使用されるAndroid Studio実行構成をコピーします。

MacOS / Linux:

mkdir -p .idea/runConfigurations
cp runConfigurations/* .idea/runConfigurations/

Windows:

MKDIR .idea\runConfigurations
COPY runConfigurations\* .idea\runConfigurations\

コマンドを実行すると、各ステップの実行構成が表示されます。
image.png

  3. デバイスまたはエミュレータでステップ1の構成を実行します。

アプリが実行され、次のスクリーンショットのような画面が表示されます。
image.png

  4. 画面を回転させて、タイマーがリセットされることに注意してください!

Android 8.0以降を実行しているエミュレーターまたはデバイスを使用している場合は、設定で回転を有効にする必要があります。
image.png

次に、アプリを更新して、画面の回転が変わっても状態を維持する必要があります。このクラスのインスタンスは、画面の回転などの構成の変更に耐えるため、ViewModelを使用できます。

3.ステップ2-ViewModelを追加する

このステップでは、ViewModelを使用して、画面の回転をまたいで状態を維持し、前のステップで観察した動作に対処します。前のステップでは、タイマーを表示するアクティビティを実行しました。このタイマーは、画面の回転などの構成の変更によりアクティビティが破壊されるとリセットされます。
ViewModelを使用して、アクティビティまたはフラグメントのライフサイクル全体にわたってデータを保持できます。前のステップが示すように、アクティビティはアプリデータを管理するのに適切な選択肢ではありません。アクティビティとフラグメントは、ユーザーがアプリを操作するたびに頻繁に作成および破棄される短命のオブジェクトです。ViewModelは、ネットワーク通信に関連するタスクの管理、およびデータの操作と永続化にも適しています。

ViewModelを使用してクロノメーターの状態を永続化する

ChronoActivity2を開き、クラスがViewModelを取得して使用する方法を調べます。

ChronometerViewModel chronometerViewModel
                = new ViewModelProvider(this).get(ChronometerViewModel.class);

これは、LifecycleOwnerのインスタンスを指します。LifecycleOwnerのスコープが有効である限り、フレームワークはViewModelを有効に保ちます。画面の回転などの構成変更のために所有者が破棄された場合、ViewModelは破棄されません。次の図に示すように、所有者の新しいインスタンスは既存のViewModelに再接続します。
image.png

注意:アクティビティまたはフラグメントのスコープは、作成から終了(または終了した)に移行します。これは、破壊と混同しないでください。デバイスが回転すると、アクティビティは破棄されますが、それに関連付けられているViewModelのインスタンスは破棄されないことに注意してください。

やってみて

アプリを実行し([実行構成]ドロップダウンでステップ2を選択)、次のいずれかのアクションを実行したときにタイマーがリセットされないことを確認します。

  1. 画面を回転させます
  2. 別のアプリに移動してから戻ります

image.png
ただし、ユーザーまたはシステムがアプリを終了すると、タイマーがリセットされます。

注意:システムは、フラグメントやアクティビティなどのライフサイクル所有者のライフサイクル全体にわたって、ViewModelのインスタンスをメモリに保持します。システムはViewModelのインスタンスを長期ストレージに永続化しません。

4.ステップ3-LiveDataを使用してデータをラップする

この手順では、前の手順で使用したクロノメーターを、Timerを使用するカスタムのものに置き換え、UIを毎秒更新します。Timerは、繰り返しタスクをスケジュールするために使用できるjava.utilクラスです。このロジックをLiveDataTimerViewModelクラスに追加し、アクティビティを残して、ユーザーとUI間の対話の管理に集中します。

タイマーによって通知されると、アクティビティはUIを更新します。メモリリークを防ぐため、ViewModelにはアクティビティへの参照が含まれていません。たとえば、画面の回転などの構成の変更により、ガベージコレクションが必要なアクティビティへのViewModelの参照が発生する場合があります。システムは、対応するアクティビティまたはライフサイクルの所有者が存在しなくなるまで、ViewModelのインスタンスを保持します。

注意ViewModelコンテキストまたはビューへの参照を保存すると、メモリリークが発生する可能性があります。ContextクラスまたはViewクラスのインスタンスを参照するフィールドは避けてください。onCleared()メソッドは、ライフサイクルが長い他のオブジェクトへの参照をサブスクライブ解除またはクリアするのに役立ちますが、ContextオブジェクトまたはViewオブジェクトへの参照をクリアするためには役立ちません。

ViewModelからビューを直接変更する代わりに、アクティビティまたはフラグメントを構成して、データソースを監視し、変更時にデータを受信します。この配置は、オブザーバーパターンと呼ばれます。

:データをオブザーバブルとして公開するには、LiveDataクラスで型をラップします。

データバインディングライブラリ、またはRxJavaのような他のリアクティブライブラリを使用したことがある場合は、オブザーバーパターンに慣れているかもしれません。LiveDataは、ライフサイクルに対応した特別なオブザーバブルクラスであり、アクティブなオブザーバーにのみ通知します。

LifecycleOwner

ChronoActivity3は、ComponentActivityのサブクラスであるAppCompatActivityのインスタンスです。リファレンスドキュメントを見て、ComponentActivityLifecycleOwnerを実装していることに注意してください。
LifecycleOwnerは、Androidライフサイクルを持つすべてのクラスで使用されるインターフェースです。実際には、クラスにライフサイクルを表すLifecycleオブジェクトがあることを意味します。
ViewModelLiveDataの両方がライフサイクルにバインドできます。

ChronoActivityを更新する

 1. subscribe()メソッドでChronoActivity3クラスに次のコードを追加して、サブスクリプションを作成します。

mLiveDataTimerViewModel.getElapsedTime().observe(this, elapsedTimeObserver);

 2. 次に、LiveDataTimerViewModelクラスで新しい経過時間値を設定します。 次のコメントを見つけます。

//TODO post the new value with LiveData.postValue()

コメントを次の文に置き換えます。

mElapsedTime.postValue(newValue);

 3. アプリを実行し、Android StudioでLogcatを開きます。別のアプリに移動しない限り、ログは毎秒更新されることに注意してください。お使いのデバイスがマルチウィンドウモードをサポートしている場合は、使用してみてください。画面を回転しても、アプリの動作には影響しません。
image.png

LiveDataオブジェクトは、アクティビティまたはLifecycleOwnerがアクティブな場合にのみ更新を送信します。別のアプリに移動すると、戻るまでログメッセージが一時停止します。LiveDataオブジェクトは、それぞれのライフサイクル所有者がSTARTEDまたはRESUMEDの場合にのみ、サブスクリプションがアクティブであると見なします。

5.ステップ4-ライフサイクルイベントの講読

多くのAndroidコンポーネントとライブラリでは、次のことが必要です。

  1. コンポーネントまたはライブラリを講読または初期化します。
  2. コンポーネントまたはライブラリを講読解除または停止します。

上記の手順を完了しないと、メモリリークや微妙なバグが発生する可能性があります。
ライフサイクル所有者オブジェクトをライフサイクル対応コンポーネントの新しいインスタンスに渡して、ライフサイクルの現在の状態を確実に把握できます。
次のステートメントを使用して、ライフサイクルの現在の状態を照会できます。

lifecycleOwner.getLifecycle().getCurrentState()

上記のステートメントは、Lifecycle.State.RESUMEDLifecycle.State.DESTROYEDなどの状態を返します。
LifecycleObserverを実装するライフサイクル対応オブジェクトは、ライフサイクル所有者の状態の変化も監視できます。

lifecycleOwner.getLifecycle().addObserver(this);

オブジェクトに注釈を付けて、必要なときに適切なメソッドを呼び出すように指示できます。

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void addLocationListener() { ... }

ライフサイクル対応コンポーネントを作成する

このステップでは、アクティビティライフサイクルの所有者に反応するコンポーネントを作成します。フラグメントをライフサイクルの所有者として使用する場合、同様の原則と手順が適用されます。

AndroidフレームワークのLocationManagerを使用して、現在の緯度と経度を取得し、ユーザーに表示します。 この追加により、次のことが可能になります。

  • 変更を講読し、LiveDataを使用してUIを自動的に更新します。
  • アクティビティのステータスの変更に基づいて、登録および登録解除するLocationManagerのラッパーを作成します。

通常、LocationManagerをアクティビティのonStart()またはonResume()メソッドの変更にサブスクライブし、onStop()またはonPause()メソッドでリスナーを削除します。

// Typical use, within an activity.

@Override
protected void onResume() {
    mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, mListener);
}

@Override
protected void onPause() {
    mLocationManager.removeUpdates(mListener);
}

このステップでは、BoundLocationManagerと呼ばれるクラスを更新してライフサイクルを認識します。LifecycleOwnerの変更にバインド、監視、および対応します。
クラスがアクティビティのライフサイクルを監視するには、それをオブザーバとして追加する必要があります。これを実現するには、次のコードをコンストラクタに追加して、ライフサイクルを監視するようにBoundLocationManagerオブジェクトに指示します。

lifecycleOwner.getLifecycle().addObserver(this);

ライフサイクルの変更が発生したときにメソッドを呼び出すには、@OnLifecycleEventアノテーションを使用できます。BoundLocationListenerクラスの次の注釈を使用して、addLocationListener()およびremoveLocationListener()メソッドを更新します。

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
void addLocationListener() {
    ...
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
void removeLocationListener() {
    ...
}

:オブザーバーはプロバイダーの現在の状態に移行するため、コンストラクターからaddLocationListener()を呼び出す必要はありません。 オブザーバーがライフサイクルの所有者に追加されたときに呼び出されます。

アプリを実行し、デバイスを回転させたときにログモニターに次のアクションが表示されることを確認します。

D/BoundLocationMgr: Listener added
D/BoundLocationMgr: Listener removed
D/BoundLocationMgr: Listener added
D/BoundLocationMgr: Listener removed

Androidエミュレータを使用して、デバイスの場所の変更をシミュレートします(3つのドットをクリックして、拡張コントロールを表示します)。TextViewは、変更されると更新されます。
image.png

6.ステップ5-フラグメント間でViewModelを共有する

フラグメント間でViewModelを共有する

ViewModelを使用して次の追加手順を実行し、フラグメントと次の間の通信を有効にします。

  • 単一のアクティビティ
  • フラグメントの2つのインスタンス。それぞれにSeekBarがあります。
  • LiveDataフィールドを持つ単一のViewModel

このステップを実行し(実行構成で「ステップ5」を選択)、互いに独立したSeekBarの2つのインスタンスに注目してください。

image.png
1つのSeekBarが変更されたときに、他のSeekBarが更新されるように、フラグメントをViewModelに接続します。
image.png

:各フラグメントのライフサイクルは独立しているため、アクティビティをライフサイクルの所有者として使用する必要があります。

この演習用のステップバイステップのマニュアルはありませんが、step5_solutionパッケージで解決策を見つけることができます。

7.ステップ6-プロセスの再作成にわたってViewModelの状態を保持する(ベータ)

このセクションで使用されるモジュールはベータ段階です。 これは、APIが将来変更される可能性があることを意味します。
メモリ管理の概要から:

ユーザーがアプリを切り替えると、Androidはフォアグラウンドではない(つまり、ユーザーに表示されず、音楽再生などのフォアグラウンドサービスを実行していない)アプリを、最近使用されていない(LRU)キャッシュに保持します。たとえば、ユーザーが最初にアプリを起動すると、そのためのプロセスが作成されます。 しかし、ユーザーがアプリを離れると、そのプロセスは終了しません。システムはプロセスをキャッシュに保持します。ユーザーが後でアプリに戻ると、システムはプロセスを再利用するため、アプリの切り替えが速くなります。

システムのメモリが不足すると、キャッシュ内のプロセスのうち使用頻度が低いものから順に削除されます。ユーザーがアプリに戻ると、システムはアプリを新しいプロセスで再起動します。
これは、ユーザーがしばらくアプリと対話していない場合にのみ発生するため、アプリに戻って初期状態で見つけることは許可される場合があります。ただし、プロセスの終了時にその情報が失われないように、アプリまたはその一部の状態を保存したい場合があります。
lifecycle-viewmodel-savedstateモジュールは、ViewModelsに保存された状態へのアクセスを提供します。
このモジュールのgradle依存関係は

"androidx.lifecycle:lifecycle-viewmodel-savedstate:$savedStateVersion"

依存関係を取得したら、デフォルトのフラグメントまたはアクティビティを使用している限り、ViewModelSavedStateHandleにアクセスできます。SavedStateHandleは、プロセスの終了後も存続するキーと値のマッピングです。
まず、これらの変更なしでステップ6を試してみましょう。

 1. 実行構成「ステップ6」を開きます

image.png
簡単なフォームが表示されます。
image.png

 2. 名前を変更して「保存」をクリックします。 これにより、ViewModel内のLiveDataに保存されます。
image.png

 3. プロセスを強制終了するシステムをシミュレートします(P +を実行するエミュレーターが必要です)。

まず、次のように入力してプロセスが実行されていることを確認します。

$ adb shell ps -A |grep lifecycle

これにより、com.example.android.codelabs.lifecycleという名前の実行中のプロセスが出力されます。

デバイスまたはエミュレータで[ホーム]を押して、次を実行します。

$ adb shell am kill com.example.android.codelabs.lifecycle

もう一度入力

$ adb shell ps -A |grep lifecycle

何も得られないはずです。これはプロセスが正しく強制終了されたことを示します。

 4. アプリをもう一度開きます(アプリランチャーでLC Step6を探します)。

image.png

ViewModelの値は永続化されませんでしたが、EditTextはその状態を復元しました。 これはどのように可能ですか?

EditTextを含む一部のUI要素は、独自のonSaveInstanceState実装を使用して状態を保存します。この状態は、プロセスが構成変更後に復元されたのと同じ方法で強制終了された後に復元されます。詳細については、ViewModels: Persistence, onSaveInstanceState(), Restoring UI State and Loadersを参照してください。

実際、lifecycle-viewmodel-savedstateモジュールもonSaveInstanceStateonRestoreInstanceStateを使用してViewModel状態を永続化しますが、これらの操作はより便利になります。

ViewModelの保存状態を実装する

SavedStateViewModel.javaファイルで、SavedStateHandleを取得してプライベートフィールドに状態を保存する新しいコンストラクターを追加する必要があります。

private SavedStateHandle mState;

public SavedStateViewModel(SavedStateHandle savedStateHandle) {
   mState = savedStateHandle;
}

モジュールのLiveDataサポートを使用して、`ViewModelLiveDataを保存および公開する必要がなくなりました。 既存のゲッターとsaveNewNameを次のように置き換えます。

private static final String NAME_KEY = "name";

// Expose an immutable LiveData
LiveData<String> getName() {
    return mState.getLiveData(NAME_KEY);
}

void saveNewName(String newName) {
    mState.set(NAME_KEY, newName);
}

mStateLiveDataを使用しているため、MutableLiveData名は使用されなくなり、削除できます。
これで、同じプロセスをもう一度試すことができます。 アプリを開き、名前を変更して保存します。次に、ホームを押して、プロセスを次のように強制終了します

$ adb shell am kill com.example.android.codelabs.lifecycle

アプリを再度開くと、ViewModelの状態が今回保存されていることがわかります。
image.png

SavedStateHandlerを使用すると、プリミティブ、バンドル、Parcelables、Serializablesおよびその他のタイプのデータを保存および復元できます。

詳細については、ViewModelドキュメントのSaved Stateモジュールをご覧ください。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1