Contextを 理解する by Okumura DroidKaigi 2024
Okumura X (Twitter) : @okmr_okok ● Androidエンジニア ● 合同会社DMM.com
Context?
例えば文字列リソースを取得するときの これ
本セッションのゴール 以下がわかるようになること ● Contextとはなにか ● Contextの使い分け方 ● Contextを使う上での注意点
本セッションの対象者 ● Android開発に興味がある方 ● Android開発初学者の方 ● Contextをなんとなく使っているという方
Contextとは?
Contextとは (訳) アプリケーション環境に関するグローバル情報へのインターフェース。 これは抽象クラスで、その実装は Androidシステムによって提供されます。 アプリケーション固有のリソースやクラスにアクセスできるほか、アクティビティの起動、インテントのブロード キャストや受信など、アプリケーションレベルの操作のアップコールも可能です。 出典:https://developer.android.com/reference/android/content/Context
Contextとは アプリがアプリ内外の様々な情報や機能にアクセスするための窓口のようなもの アプリ Androidシステム Context アプリ情報 ・リソース ・テーマ ︙ ・システムサービス ・システム設定 ・ストレージ ︙
Contextの使用例
Contextの使用例 ● リソースへのアクセス ● 通知の作成 ● データベースへのアクセス etc… 他にもたくさんあります
Contextってなんで必要なの?
例1. 文字列リソースの取得 Contextがアプリケーションの現在の環境に基づいて 適切なリソースを提供
例1. 文字列リソースの取得 ● res/values/strings.xml(デフォルトの言語用) ● res/values-en/strings.xml(英語用) ● res/values-es/strings.xml(スペイン語用)
例1. 文字列リソースの取得
例1. 文字列リソースの取得 android.content.Context#getString(int) (直訳) アプリケーションのパッケージのデフォルトの文字列テーブルから ローカライズされた文字列を返します。
Contextってなんで必要なの? 文字列リソースへのアクセスをする場合: ● 同じリソースIDでも端末のロケールによって返却される文字列 が変わる ● Contextが無いとどの言語の文字列を返せばよいかがわから ない
例2. 通知の作成&表示 通知を作成する場合、Contextを通じて通知に必要なシステム サービス(NotificationManager)にアクセス Contextは通知のレイアウトやリソースの取得もサポートする
例2. 通知の作成&表示 通知の表示までの流れ 1. 通知マネージャを取得 2. 通知チャンネルを作成 3. 通知を作成 4. 通知を表示
例2. 通知の作成&表示
例2. 通知の作成&表示
例2. 通知の作成&表示
例2. 通知の作成&表示
Contextってなんで必要なの? 通知の作成&表示をする場合: ● Contextを通じてNotificationManagerにアクセスすること で、通知チャンネルの作成や通知の表示を実現 ● 通知を作成する際に必要なリソースを取得するためにも使わ れる
Contextとは(再) アプリがアプリ内外の様々な情報や機能にアクセスするための窓口のようなもの アプリ Androidシステム Context アプリ情報 ・リソース ・テーマ ︙ ・システムサービス ・システム設定 ・ストレージ ︙
Contextの種類と使い分け
Contextの種類と使い分け そもそもContextは抽象クラス
Contextの種類と使い分け 継承しているクラスは多くあるが、特に重要なものが2つ
Contextの種類と使い分け 継承しているクラスは多くあるが、特に重要なものが2つ
依存関係 Context ContextWrapper ContextThemeWrapper Application Activity
Contextの種類と使い分け Activity(便宜上、以降は ActivityContext と記載) Activityのライフサイクルに依存 Application(便宜上、以降は ApplicationContext と記載) アプリのライフサイクルに依存
Contextの種類と使い分け ActivityContext Activityのライフサイクルに依存 【用途】 ● Activityのライフサイクル内での短期的な処理 ● Activityの情報が必要な処理 ○ 例:Activityの起動、個別のActivityのテーマに紐づくUI要素の表示
Contextの種類と使い分け ApplicationContext アプリのライフサイクルに依存 【用途】 ● Activityのライフサイクルを超えて行われる処理 ○ 例:Workerなどのバックグラウンド処理の起動、データベースへのアクセ ス
誤ったContextを使うとどうなるの?
Activityの情報が必要な処理でApplicationContextを使った場合 例:Activityを起動するケース
Activityの情報が必要な処理でApplicationContextを使った場合 例:Activityを起動するケース クラッシュ
Activityの情報が必要な処理でApplicationContextを使った場合 例:Activityを起動するケース (直訳)アクティビティ コンテキストの外部から startActivity() を呼び出すには、 FLAG_ACTIVITY_NEW_TASK フラグが必要です。これは本当にあなたが望むものですか?
Activityのライフサイクルを超えて行われる処理でActivityContextを使った場合 例:Workerでバックグラウンドで処理を行うケース 【予想】Activity破棄後にContextにアクセスしようとしたときにク ラッシュする👀
Activityのライフサイクルを超えて行われる処理でActivityContextを使った場合 例:Workerでバックグラウンドで処理を行うケース 【予想】Activity破棄後にContextにアクセスしようとしたときにク ラッシュする👀 【結果】せず!!!!!!
Activityのライフサイクルを超えて行われる処理でActivityContextを使った場合 例:Workerでバックグラウンドで処理を行うケース 【予想】Activity破棄後にContextにアクセスしようとしたときにク ラッシュする👀 【結果】せず!!!!!! なんで??? ��
Activityのライフサイクルを超えて行われる処理でActivityContextを使った場合 androidx.work.WorkManager#getInstance(android.content.Context) の中身見てみる
Activityのライフサイクルを超えて行われる処理でActivityContextを使った場合 WorkManagerImplのgetInstanceメソッドに渡している WorkManagerImplのgetInstanceメソッドも見てみる
内部でApplicationContext取得してた
Activityのライフサイクルを超えて行われる処理でActivityContextを使った場合 ● 実際には、今回のようにActivityContextを渡してしまっても問 題ないケースもあるよう ● とはいえ全てのライブラリがそうとは限らないので、Activityの ライフサイクルを超える処理にはApplicationContextを使っ ておくのが無難(私の考え)
メモリリーク
メモリリークとは? ● メモリを必要以上に確保し続けることでメモリが無駄に消費さ れる現象 ● 他の処理に使えるメモリが減るのでアプリのパフォーマンスが 低下する
ActivityContextがメモリリークを起こす例 Activityよりも寿命が長いViewModelでActivityContextのインスタンスを保持 警告が出る
用途・ライフサイクルを意識して 正しいContextを選択しよう
Contextの取得方法
Contextの取得方法 ● AndroidView ○ Activity編 ○ Fragment編 ● JetpackCompose編
AndroidView Activity編
ActivityでのContextの取得方法 「this」で自身(ActivityContext)を取得
ActivityでのContextの取得方法 applicationContextもそのまま取得できる
AndroidView Fragment編
【補足】Fragmentとは AndroidアプリのUIの一部を構成するためのコンポーネント Activityや他のFragmentによって動かされる Activity Fragment
FragmentでのContextの取得方法 ● getContext() ● requireContext() ● getActivity() ● requireActivity()
getXXXとrequireXXXの違い ● getContext() ● requireContext() ● getActivity() ● requireActivity()
getXXXとrequireXXXの違い ● getContext、getActivity ○ Contextが存在しない場合はnullを返す ● requireContext、requireActivity ○ Contextが存在しない場合は例外 を吐く
Contextが存在しないとは Fragment が Activity または他の Fragment に 結び付けられていない(アタッチされていない)状態
どう使い分けたらいいのか
どう使い分けたらいいのか アタッチされていることが確実な場合は requireXXX を使った方 が簡潔に書ける(わざわざ getXXX を使ってnullチェックをする必 要はない)
アタッチされていることが確実なのは具体的にどういう場面?
アタッチされていることが確実じゃない場合、どうしたらよいか 1. getXXX を使いnull チェックをする
アタッチされていることが確実じゃない場合、どうしたらよいか 2. isAdded() によってアタッチされているかどうかを判定してか ら requireXXX を使う
XXXContextとXXXActivityの違い ● getContext() ● requireContext() ● getActivity() ● requireActivity()
XXXActivityとXXXContextの違い ● getActivity、requireActivity ○ Activity特有の機能にアクセスしたいとき ■ 例:Activityの起動、Activityに定義しているメソッドの呼び出し ● getContext、requireContext ○ Activityである必要がないとき
FragmentでのContextの取得方法のまとめ XXXContext getXXX requireXXX getContext requireContext ● アタッチされていない可能性がある ● アタッチされていることが確実 ● Activity特有の機能にアクセスする必要 ● Activity特有の機能にアクセスする必要が がない XXXActivity getActivity ない requireActivity ● アタッチされていない可能性がある ● アタッチされていることが確実 ● Activity特有の機能にアクセスする必要 ● Activity特有の機能にアクセスする必要が がある ある
JetpackCompose編
Contextの取得方法(JetpackCompose) LocalContextを使用
Contextの取得方法(JetpackCompose) ApplicationContextはこう
LocalContextについて Compose の CompositionLocal という仕組みを使って現在の Context を提供
LocalContextについて Compose の CompositionLocal という仕組みを使って現在の Context を提供 CompositionLocal はツリー構造の中で値を受け渡す仕組み で、自分で値をパスしていかなくてもLocalContext によって Context が自動的に渡され、どこでも使えるようになっている
余談 JetpackComposeではフレームワークが Context を自動的に管理しているので Contextを指定する場面は従来よりも少ない Composable関数でのDialogの例 AndroidViewでのDialogの例
ケーススタディ
問題① ActivityContextを使用する適切なシナリオはどれですか? 1. アクティビティに依存する機能にアクセスする 2. アプリケーション全体で共有するリソースにアクセスする 3. バックグラウンド処理を行う
答え ActivityContextを使用する適切なシナリオはどれですか? 1. アクティビティに依存する機能にアクセスする 2. アプリケーション全体で共有するリソースにアクセスする 3. バックグラウンド処理を行う
問題② ActivityContextを使用する際にメモリリークを防ぐための適切な 方法はどれですか? 1. ActivityContextを短期間で使用し、長期間保持しない 2. ActivityContextをアプリケーション全体で共有する 3. ActivityContextを静的変数として保持する
答え ActivityContextを使用する際にメモリリークを防ぐための適切な 方法はどれですか? 1. ActivityContextを短期間で使用し、長期間保持しない 2. ActivityContextをアプリケーション全体で共有する 3. ActivityContextを静的変数として保持する
問題③ Fragmentがアタッチされていることが確実な場合、どのメソッドを 使ってContextにアクセスすべきですか? 1. requireContext() 2. getContext() 3. getApplicationContext() 4. LocalContext.current
答え Fragmentがアタッチされていることが確実な場合、どのメソッドを 使ってContextにアクセスすべきですか? 1. requireContext() 2. getContext() 3. getApplicationContext() 4. LocalContext.current
まとめ
Contextとは(再々) アプリがアプリ内外の様々な情報や機能にアクセスするための窓口のようなもの アプリ Androidシステム Context アプリ情報 ・リソース ・テーマ ︙ ・システムサービス ・システム設定 ・ストレージ ︙
ライフサイクルや役割を意識して 正しくContextを使っていきましょう!
ありがとうございました