2019/09/15

Androidアプリ開発:リソース、ダイアログ、Preferencesを扱うときのContext

Androidアプリを作っていると、ダイアログで利用者の応答を得たり、SharedPreferencesで設定を保持したり、したくなる。というか、する。また、表示するメッセージやUIコンポーネントに張り付ける文字列はリソースで定義する、ということをする。

これらのことをしようとすると、Contextというオブジェクトに触ることになる。Contextは、アプリケーションの構成要素が共通に必要な情報を保持するオブジェクト、ということなのだが、これがとても初心者には難しい。正直、いまだによくわからない。

アプリケーションのグローバルな情報ということなので、設定を保持するSharedPreferences、文字列を保持するリソースへのアクセスのインタフェイスを提供するのは分かるのだが、アプリケーションからアクセス可能なContextオブジェクトは複数ある、と言われると、訳がわからなくなる。

Contextオブジェクトはアプリケーションに一つではなくて、Activity, Application, ServiceといったクラスがContextクラスを拡張していて、これらのクラスのオブジェクトがContextオブジェクトそのものなのだ。まぁ、訳が分からない。グローバルな環境情報は、一つではないのか。

なぜこうなっているかというと、Activity, Application, Serviceのそれぞれで共通に必要な情報が微妙に違うから、ということらしい。例えば、UIのスタイルを決めるThemeの仕組みがある。Themeは、アプリケーションのManifestで定義される共通情報だが、ThemeはApplicationのレベル、Activityのレベルで定義できるため、それぞれのContextの実装で保持するようになっているようだ。

正直、わかったような、わからないような。

アプリを作り始めた人が最初にContextに触るのは、SharedPreferencesへのアクセスと、リソース化した文字列の取得ではないかと思う。通常、PreferenceManager#getDefaultSharedPreferencesで、SharedPreferencesオブジェクトを取得できる。ただ、このメソッドの引数としてContextオブジェクトを渡す必要がある。Activityクラスの中でSharedPreferencesオブジェクトを取得する場合は、thisを渡せばオブジェクトが取得できる。

アプリの作りが進むと、どうしてもActivityの実装がごちゃごちゃするので、データの扱いを別のクラスで管理しようということになる。そうすると、SharedPreferencesをラップするようなstaticなクラスをつくったほうが良いかな(あるいはsingletonパターンを使うか)、という感じに必ずなる。で、このクラスの中の処理でPreferenceManager#getDefaultSharedPreferencesを実行しようして、Contextはどこから持ってくるの? ということで悩むことになる。

じゃあ、初期化の際にこのクラスにActivityオブジェクトの参照を渡せばよいのかというと、ちょっと気持ちが悪い。ActivityのonCreateはUIコンポーネントの初期化に専念させたい。他のオブジェクトの初期化処理をここに入れるのは、なんだか気持ちがよくない。仮に、アプリケーションを起動するときの入り口が複数ある場合、この初期化をすべての入り口に書かなければならなくなる。そんなケースがあるのか? と思うかもしれないが、ある。私が作ったユーティリティが、実際、アプリ起動の入り口が2つある。

じゃあどういう手があるかというと、Applicationクラスを独自に拡張したクラスを作って、staticメソッドでそのインスタンスを取得できるようにしておく、という方法がよく使われるようだ(Manifestにこのクラスを登録しておく必要がある)。このクラスのオブジェクトは、アプリケーションが起動する際にシステムによって生成されて、アプリケーションが稼働している間、存在している。これのonCreateが、アプリケーション起動時に呼ばれるコールバックなので、Activityのライフサイクルに関係なく、ここにグローバルな初期化処理を記述できる。そして、Applicationクラスの拡張なので、getApplicationContextでコンテクストが取得できる。

これで、Contextが必要な時はすべてここから取得して解決、かというとそうでもなくて、ActivityやViewに係る処理でContextが必要な場合は、Activityをコンテクストを使わないといけない。ダイアログ作成で必要なContextも、Activityの方。

Application ContextからActivity Contextを取り出せるような形になっていれば、すっきりわかりやすいように思うのだが、、。そうもいかない理由があるのであろう。




0 件のコメント :

コメントを投稿