June 2, 2012

Context-applicationとContext-activityの使い分け

ContextはいろいろなAPIから要求されますが、どんな方法でContextを引き渡していますか?
  1. this(Activityクラスの中で)
  2. getApplicationContext()
正解はAPIによって適切なほうを使う必要がある、です。ちなみにgetBaseContext()もありますが、これは使途不明なため使うべきではないと思います。

クラスとしてはどちらも同じContextですが、インスタンスとしては全く別ものです。Application、Activityそれぞれがそれぞれ自身のContextインスタンスを保持しており、中身も当然異なります。同じContextクラスだからといって、これを意識せずに使うと例外エラーやメモリリークなどいろいろな不具合を引き起こすことになります。

Android APIの例で考えると、ToastクラスではToast.make()でContextを要求しますが、これはどちらのContextでも動作します。しかし、DatePickerDialogクラスではコンストラクタでContextを要求しますが、ActivityのContextを渡さないと動作しません。ApplicationのContextを渡すと例外エラーを引き起こします。Contextを要求するAPIを使う時はどのContextを使うべきか、まずドキュメントを確認するべきです。困ったことにAPIドキュメントにも明確にどのContextを使えと記載していない場合もありますが。

別の例で、ActivityのContextを参照するようなstatic変数を使ってしまった場合、Activityが終了するときに参照をきちんと削除しないと、参照が残っているのでContextを削除することができず、メモリリークが発生します。特にstaticなAndroidクラスを使う場合、知らないうちに内部でActivityのContextが参照されてしまい、一見わかりにくいケースもありますので、そのような場合は特に注意が必要です(参考にあるサイトを参照)。

とりあえず使い分けのキーワードは「ライフサイクル」です。ContextとそのContextを参照するオブジェクトのライフサイクルは同じであるべきで、片方のオブジェクトが削除されるときは、もう一方もきちんと削除されるように作るべきです。当たり前のように聞こえますが、うっかりミスなどで簡単にリークする上に、同じContextクラスなのでビルドエラーもでなく、メモリリークはわかりにくい、と実は細心の注意が必要です。

Activityのインスタンスは頻繁に生成と削除が行われます。画面の向きを変えただけでも発生します。逆にApplicationのインスタンスは基本的に永続します。それぞれのもつContextも同じライフサイクルになります。なので、どちらに属するべきかを考慮し、属した方のライフサイクルにあわせて作り込めばメモリリークのないアプリを作ることができます。なんでもかんでも永続させるとメモリリークはしなくてもメモリを大量に使用するアプリになるので、あくまでも適切なほうのライフサイクルであるべきです。

参考:Android DevelopersのBlog:Avoiding memory leaks

No comments:

Post a Comment