blog-Android_Architecture_Componentsで犯しがちな5つの間違い

Android Architecture Componentsで犯しがちな5つの間違い【翻訳】

この記事は著者 Michał Baran(@BaranMichal25) 氏の許可を得て翻訳したものです。

Original article: 5 common mistakes when using Architecture Components

---

大なり小なり重大な結果を引き起こす微妙な間違いは(たとえそのような間違いを犯してこなかったとしても)、将来起こるかも知れない問題を避けるために、頭に入れておいたほうが良い。

この記事では、以下の犯しがちな5つの間違いについて解説する。

・FragmentでのLiveData observerのリーク
・画面のローテーション後のデータリロード
・ViewModelのリーク
・MutableなLiveDataのViewへの公開
・構成が変わる度に再作成されるViewModelの依存関係

1. FragmentでのLiveData observerのリーク

Fragmentはトリッキーなライフサイクルを持っていて、detachやre-attachで常に破棄される訳ではない。例えば、保持されたFragmentは構成の変更では破棄されない。これが起きると、Fragmentのインスタンスは生き残り、Viewだけが破棄される。そして`onDestory()`は呼ばれず、DESTROYEDの状態に達しない。

これがどういうことかというと、例えば以下のようにLiveDataを`onCreateView()`やそれ以後(よくあるのは`onActivityCreated()`)でLifecycleOwnerとしてFragmentを渡して監視し始めたときに、問題が起きる。

これはFragmentが再適用される度に新しいObserverインスタンスを渡すことになるが、LiveDataは前のObserverを破棄しない。なぜなら、LifecycleOwner(この例だとFragment)はDESTROYEDの状態になっていない。最終的にアクティブなObseverの数がどんどん増えていき、`onChanged()`で同じコードが何度も実行されることになる。

この問題はもともとここで報告され、さらに詳しい説明がここにある。

おすすめの解決方法はFragmentのView Lifecycleには、Support Library 28.0.0とAndroidX 1.0.0で追加された getViewLifecycleOwner()getViewLifecycleOwnerLiveData() を使うことだ。これでLiveDataはFragmentのViewが破棄される度にObserverも破棄してくれる。

2. 画面のローテーション後のデータリロード

Activityの初期化やセットアップは`onCreate()`(Fragmentだと`onCreateView()`かそれ以後)に書くことが多い。そしてそこに、ViewModelを介したデータのロードも書きたくなる。しかしそこに書いたことで、(ViewModelを使っていたとしても)画面がローテーションする度にデータのリロードが発生してしまう。これはほとんどの場合、無意識に意図せず起こってしまっている。

この解決方法は実装による。例えば、Repositoryがデータをキャッシュしている場合、上のコードで問題ない。

- AbsentLiveDataのようなものを使って、データがないときにだけロードする
- 実際に必要になったとき(OnClickListenerの中など)にロードする
- 最もシンプルなのが、ViewModelのコンストラクターでロード処理をして、純粋なGetterだけ公開する

3. ViewModelのリーク

ViewModelにViewの参照を渡すべきではないことは、すでに取り上げられている

さらにViewModelに他のクラスの参照を渡すことにも、慎重になったほうが良い。Activity(やFragment)が終了したあと、ViewModelはガベージコレクタに破棄されるので、ViewModelはActivityよりも長生きするオブジェクトの参照を持つべきではない。

この例では、ViewModelでシングルトンのRepositoryにリスナーを渡していて、その後参照をクリアしていないので、リークする可能性がある。

この解決方法では、Repositoryでは弱い参照で持ち、RepositoryとViewModel間でやりとりして、`onCleared()`メソッドでリスナーを破棄している。基本的には正しくガベージコレクションされればやりやすい方法で良い。

4. MutableなLiveDataのViewへの公開

これはバグではないが、関心事の分離に反している。FragmentやActivityなどのViewはLiveDataやこ自身の状態を更新できるべきではない。なぜならそれはViewModelの責務だからだ。ViewはLiveDataの監視だけをするべきだ。

したがって、MutableLiveDataは例えばGetterやバッキングプロパティなどでカプセル化すべきである。

5. 構成が変わる度に再作成されるViewModelの依存関係

ViewModelは画面のローテーションのような構成の変更があっても生き残る。そのため変更が生じる度に依存関係を作るのはシンプルに冗長だし、特に依存関係のコンストラクタのロジックで意図しない動きをしてしまうことがある。これはかなり明白かもしれないが、作成するViewModelと同じ依存関係を持つ、ViewModelFactoryを使うときに見落としやすい。

ViewModelProviderはViewModelのインスタンスを保持するが、ViewModelFactoryのインスタンスは保持しない。なので、このようなコードがあると問題が起きる。

構成の変更がある度に、ViewModelFactoryの新しいインスタンスが作成され、それに従って不必要なすべての依存関係のインスタンスが作成される(それが何らかの形でスコープされてないと仮定)。

このときの解決策は`create()`メソッドが実際に呼び出されるまで、依存関係の作成をしないようにすることだ。そうすればActivityとFragmentが生きている間に1回しか呼び出されない。例えばProviderなどを使って初期化を遅延させることで解決できる。

参考資料

- ViewModels and LiveData: Patterns + AntiPatterns
- Architecture Components pitfalls — Part 1
- Android Architecture Blueprints
- 7 Pro-tips for Room
- Official documentation

---

この記事は著者 Michał Baran(@BaranMichal25) 氏の許可を得て翻訳したものです。間違いがある場合はコメントか、@d_forestまでお願いします。

Original article: 5 common mistakes when using Architecture Components

Thank you Michał!


この記事が気に入ったら、サポートをしてみませんか?気軽にクリエイターを支援できます。

note.user.nickname || note.user.urlname

良ければサポートお願いします!おいしい食材を仕入れて、家族に振る舞います!

ありがとうございます🎶
5

ふぉれ$Cooking Programmer

料理とプログラミングは人生を豊かにする。一児の父。@PARTY
コメントを投稿するには、 ログイン または 会員登録 をする必要があります。