Android メモリ リーク
How Proper Lifecycle Management Can Prevent Android Memory Leaks
OutOfMemoryException は、よくある苛立たしいバグで、アプリが予期せず終了する主要原因の 1 つです。
“Why is this happening now, if the app were working perfectly yesterday?” と、訊かれます。 OutOfMemory Exception の原因にはさまざまなものがありますが、最も一般的なものの1つはメモリ リークで、アプリ内でメモリが割り当てられ、それが解放されないことです。 この記事では、開発プロセスの重要な部分でありながら見落とされがちな、効果的なライフサイクル管理によってこのリスクを最小化する方法を説明します。
TL;DR
この記事では、以下について説明します。
- Android のメモリ リークの理由
- それを防ぐための基本ステップ
- ライフサイクル管理のより進んだ形態
- 製品のライフサイクルを管理できるツール
Android で RAM メモリ リークが発生する理由とは?
問題は単純です。 特定のオブジェクトは一定の寿命しか持たないはずで、耐用年数が過ぎたら削除する必要があります。
理論的には、このメモリは onStop または onDestroy を使用してプロセスが終了したときに破棄されるべきものです。 しかし、オブジェクト参照の誤用により、ガベージコレクタが未使用のオブジェクトの割り当てを解除できなくなることがあります。 たとえば、未使用のオブジェクト A が未使用のオブジェクト B を参照している場合、2 つの不要なオブジェクトが存在することになり、これらは互いに参照しているため、ガベージコレクタは決して割り当て解除できません。
Common Tricks to Stop This Happening
開発者は、メモリに捕捉されて死んでいく活動を止めるために多くのステップを踏むことができます。
- onResume()/onPause() または onStart()/onStop() で BroadcastReceiver を登録/解除する
- View/Activities/Contexts に静的変数は使用しない
- Context への参照を保持する必要があるシングルトンは applicationContext() を使用するか WeakReference にラップする
- Beatthe with anonymous and non-BlackReceiver in Japan, Inc.静的な内部クラスは、その包含するクラスへの暗黙の参照を保持します。
- 内部または匿名クラスがキャンセル可能な場合 (AsyncTask、Thread、RxSubscriptions など)、アクティビティが破棄されたときにそれをキャンセルします。
The Android Lifecycle Aware Components
上記の基本ステップを完了したら、次はもっと重要なこと、アプリのアクティビティのライフサイクルの話です。 ライフサイクルを正しく管理しないと、不要になったメモリにしがみつくことになります。
これには多くの異なるタスクがあります。 各アクティビティについて、スレッドを中断し、RxJava のサブスクリプションを取り除き、AsyncTask の参照をキャンセルし、このアクティビティの参照 (およびそれに接続している他のすべてのアクティビティ) を正しく削除することを確認する必要があります。 これらのタスクはすべて、開発者の時間を膨大に消耗します。
Android のユーザー インターフェイスを構築するためにしばしば採用されるアーキテクチャ パターンである Model View Presenter (MVP) によって、状況はさらに複雑になっています。 MVP パターンでは、View と Presenter の両方が、それらの間の動作に関する契約の抽象的な実装です。 MVP を実装する最も一般的な方法は、ビューの実装として Activity/Fragment を使用し、ビューの参照を持つことに慣れているプレゼンターのためのシンプルな実装を使用することです。
したがって、プレゼンター参照を持つビューとビュー参照を持つプレゼンターがあります (Hint: 私たちはここでリークの可能性があります)。
これらの潜在的な困難を考えると、ライフサイクル中に作成された過剰なメモリを削除するために、適切な管理構造を配置することが不可欠になります。 これを行うには、いくつかの実証済みの方法があります。
Creating Lifecycle-Aware Components Using Android Arch Lifecycle on Android Studio
Lifecycle-Aware Component はスマートなコンポーネントです。 アクティビティやフラグメントなどの別のコンポーネントのライフサイクルの状態の変化に反応し、たとえば、メモリを取り除くことができます。 これは、コードがより軽く、メモリ効率がはるかに良いことを意味します。
Arch Lifecycle は Android の新しいライブラリで、ライフサイクルを考慮したコンポーネントを構築するためのツール セットを提供します。 このライブラリは抽象的な方法で動作するため、ライフサイクルの所有者は特定のタスクやアクティビティのライフサイクルの管理について心配する必要がなくなります。
Arch Lifecycle の主要ツールと定義は次のとおりです。 どのオブジェクトが Android のライフサイクルを持つかを定義し、それらを監視できるようにするソートシステムです。 Android のライフサイクルを持つものとして識別された各オブジェクトを監視する通常のインターフェイスで、各主要ライフサイクル イベントを処理する簡単な数式を使用します。 LifecycleObserver インターフェースを実装するクラスで使用できるアノテーションです。 これにより、起動されるたびにアノテーションされたメソッドをトリガーする主要なライフサイクル イベントを設定することができます。 設定可能なイベントの一覧は以下の通りです。
- on_any
- on_create
- on_destroy
- on_pause
- on_resume
- on_start
- ON_STOP
これらのツールを使用して、すべてのクリーンなタスクをそのオーナー (私たちの場合はプレゼンター) に送信できるため、リークのないクリーンで非連結のコードができます (少なくともプレゼンター レイヤーでは)。
interface View: MVPView, LifecycleOwnerclass RandomPresenter : Presenter<View>, LifecycleObserver { private lateinit var view: View override fun attachView(view: View) { this.view = view view.lifecycle.addObserver(this) } @OnLifecycleEvent(Lifecycle.Event.On_DESTROY) fun onClear() {//TODO: clean }
Using Android Arch View Models as Presenters and LiveData
ライフサイクルの管理ミスによるメモリ リークを回避するもうひとつの方法は、Arch Components Library からの新しい ViewModels を使用する方法です。
ViewModel は抽象クラスで、特定のオブジェクトが削除されなければならないときに自動的に呼び出される onClear として知られる単一の関数を実装しています。 ViewModel はフレームワークによって生成され、作成者のライフサイクルに添付されます (おまけに、Dagger で注入するのは超簡単です。)
ViewModel の使用と同様に、LiveData も通信の重要なチャネルを提供します。 この製品は、反応的で観察しやすいパターンに従っているため、個々のオブジェクトがそれを観察することができます (より少ない結合コードを作成します)。
ここでの素晴らしいポイントは、LiveData がライフサイクル所有者によって観察できるため、データの転送が常にライフサイクルによって管理され、それらを使用中に参照が確実に保持されるようにできることです。
LeakCanary と Bugfender の使用
前述のステップに加えて、2 つの重要なキットを推奨したいと思います。 リークを監視する人気のツールである LeakCanary と、私たち自身の Bugfender です。
LeakCanary は Android および Java 用のメモリ検出ライブラリです。 オープンソースであるため、背後にある巨大なコミュニティがあり、リークについて教えてくれるだけでなく、考えられる原因を知らせてくれます。
Bugfender はリモート ロギング ツールで、個々の LeakTraces をデバッグし、リークが発生したときに知らせる DisplayLeakService というクラスを拡張することができます。 その後、Bugfender で簡単にログを取ることができます。
public class LeakUploadService extends DisplayLeakService { override fun afterDefaultHandling(heapDump: HeapDump, result: AnalysisResult, leakInfo: String) { if (result.leakFound) { Bugfender.d("LeakCanary", result.toString()) } }}
さらに、ユーザーは Bugfender の他のすべての利点を利用できます。24/7 ログ記録 (デバイスがオフラインの場合でも)、内蔵のクラッシュ レポート、使いやすい Web コンソール、個々のデバイスへのドリルダウンにより顧客ケアを改善できる、などです。
Leave a Reply