Contextの使い分けについて私が辿った変遷
Application Contextを渡してはいけないという記事を読みました。
https://ytrino.hatenablog.com/entry/2016/05/26/033936
この記事を読んで改めて自分のContextに対する認識があまいことを実感したので、思ったことをまとめてみようと思います。
趣旨は「僕はContextに対してこういうふうに思ってるんだけど、違う・違わないが自信持ててないから誰か突っ込んでくれ」です。たぶんそんなに間違ってないと思うんですけど、相談できるような相手がいないんだ、察しておくれ。
「私もそういう認識です」と言ってもらえれば自信になるし、違う部分があれば指摘をもらって学びの機会になり私のためになります。また、こういう変遷を経て学んできたという情報が初学者の役に立てばなぁなんて思ってもいます。
私のAndroidレベルは初学者というわけでもないですが、Contextにまつわる話を聞くと理解が曖昧でモヤモヤするレベルです。上記記事に倣えばAndroid2級でしょうか。
私は最近でこそライフサイクル考えてContextを使い分けるようになりましたが、ちょっと前まで「困ったらApplication Context渡しとけば問題ない」という考えの人でした。前までというか、今もそういう部分があるんですけどね。だからこそ「Application Contextを渡してはいけない」というのを見て不安になってしまいました。
Application Contextを渡してはいけないというのは、どういう意味なのでしょうか。
- アプリがクラッシュするから渡してはいけない
- 表示がおかしくなるから渡してはいけない
- 渡しても動作上は問題ないが、コードの見た目上・意味的に美しくないので渡してはいけない
実はApplication Contextをなるべく使おうというのも、Application Contextを渡してはいけないというのも、あるコンテキストにおいては間違ってないんじゃないかなと思います。それぞれ違うコンテキストで話しているので、そこを混同すると混乱してしまいます。
前者は「メモリリークを回避する」という文脈での話で、Activityのリークを防ぐにはApplication Contextを使えばいいのは正しい(はず)です。
一方後者はthemeの適用の観点での話で、Application Contextでは意図した動きにならない・変にハマることがあるからApplication Contextを安易に使ってはいけないという話で、これはこれで正しいわけです。
どっちも正しいのですが、残念ながらコードを書いていてContextを要求された時に、私たちは何らかのContextを渡さないと先に勧めません。何を渡せばいいかは「ケースバイケースなんで一概にどれを使えとは言えない」んで、Contextの要求に対しては、それぞれ適切なContextを渡す必要があります。
ではどのようにしたら、何を使うのが適切かを判断できるのでしょうか。ライフサイクルを考えろということでしょうか。そのライフサイクルはどうやって判断するんでしょうか。
私はその判断を、今はソースコードを読むことで行っています。Contextを要求するメソッドが、クラスが、いったい渡されたContextをどのように利用しているのか。それをもって何を渡すか考えています。
Androidを学び始めた当初は、Contextなんて意識していませんでした。本やサンプルコードの写経する分には意識しないですみますから。
しかし写経から脱し、自分でコードを書き始めるとそうもいきません。こういう処理を実装したいな→このメソッド使えばなんとかなりそう→Contextが必要らしい。はたとキーを打つ手が止まります。
そのメソッドを使うにはContextを渡すしかない。渡さないと先に進まない。とりあえずthisって書いたら動いた。よし、これで先へ進める。なんていうのが、Androidを学び始めた人が辿る道ではないでしょうか。私はそうでした。
学び始めの頃はActivityからなんかすることが多いので、this、すなわちActivityを渡していればとりあえずは動いてくれました。私はそうして「ああ、ContextっていうのはActivityを渡せばいいのだな」と認識するようになりました。
しかしActivity Contextを渡してしまうとメモリリークが発生するぞという話を耳にします。そこで登場するのがApplication Context。getApplicationContext()
を使えばメモリリーク対策になるという話を聞いて、「ああ、じゃあとりあえずApplication Contextを渡しておけば問題ないのか」となりました。
しかし今度はApplication Contextを渡すと想定通りにViewが表示されないことがあるという。無思考にApplication Contextを渡すのもどうやらダメらしい。
一周回ってきてしまいました。もう一体どうしろと。このメソッドContext要求してくるのに僕は一体何を渡せばいいんだ。
そんな私が多少なりとも指針を持てるようになったのは、一言で言ってしまえば慣れてきたからなんだと思います。
とりあえずコード書いて、動かしてみて。それを繰り返すうちに慣れてきて。ソースコードを読むようになって「多分こうやれば大きく間違ってはいないはず」という、そんな指針を持てるようになりました。
Contextを要求されたら何を渡すか。重要なのは考え方、つきあい方だと思います。これが今回の記事の本題です。
私はまずライフサイクルを考えるより前に、Contextを要求するメソッドのソースコードを確認しに行きます。そいつがContextへの参照を保持するのか、それともContextを使ってなんかするだけなのか。それを確認するのです。
Contextへの参照を保持するなら、Activity Contextを渡していいのか、ライフサイクルを考えなければなりません。ライフサイクルを考えるというよりは、Activityとともに役目を終えるのか、そうでないのかで判断していますが、正直このあたりは曖昧です。
該当のメソッドが単にStringリソースを読みだすのに使っているとかだったら、別にActivity Contextを使っても問題ないですよね。そもそもActivityがリークするのは、Activityへの参照を持ち、渡した先のオブジェクトがActivityより長生きする場合だからです。
Contextへの参照を持たないのであれば、別にどっちを使おうが問題ないので、好きな方使えばいいじゃんって感じですが、Activityから呼び出すのであれば、わざわざApplication Context渡すのも大げさなので、Activity Contextでいいかと思います。
Androidと付き合っていくうちに、なんとくこんな感じに行き着きました。それでもメモリリークが怖いなら、Contextに何を使えばいいか迷うよりも、それでメモリリークするのかを確認する方に力を注いだほうが良いと思います。
でも、独学でやってるとこれで本当に問題ないのか確証が持てないので、いつもどこかで不安です。誰かに聞く機会もなくてつらい。
そもそもContextについて意識し始めるキッカケは、私の場合はActivityのメモリリークです。
このメモリリークも私にとってはContext以上によく分からない存在でした。最初のうちはメモリリークしているかどうかをどうやって確認すればいいのかがわからなかったからです。確認方法が分からないから、メモリリークしないコードを書かなきゃという気持ちだけが肥大化していました。
そんな中で「とりあえずApplication Context渡しておけばライフサイクルの関係でActivityがリークすることはない」という1つの指針は、私にとっては心強かったです。
実際にはApplication Contextを渡すからメモリリークしなくなるわけではないというのは、今であれば分かります。しかしはじめの頃はそんな違いも分からなくて、そんなことよりContextとして何を渡せばいいのかという指針があることがありがたかったのです。
これに関しては、メモリリークしているかどうかを確認する方法を学ぶ方が大切だと、今であれば思いますけどね。当初は確認方法がわからないけど、メモリリークしたら困るからApplication Context渡しとけっていう感じでコード書いてました。
初学者がもっとも優先すべきはとりあえず先に進むことだと思います。動くアプリを作ることです。そしてAndroidに慣れることです。
Contextを要求するメソッドに対して、何らかのContextを渡す。Activity Context渡すのかApplication Context渡すのか迷うと思います。最初のうちはとりあえずどっちか渡して動いてればいいと思います。動けば正義です。迷って足を止めるより、いっぱい作ってAndroidに慣れる方が大事だと思います。
慣れてきたらContextを渡すときに「こいつはActivityより長生きする奴なのかな」と考えましょう。そんなときはソースコードを読むことが助けになると思います。
Contextを要求するメソッドは、渡されたContextをどのように使っているのか。そいつもContextが必要なメソッドを呼び出すために必要としているのか。それともContextへの参照を保持するのか。
Contextに何を使うべきなのかはケースバイケースで一概には言えません。Activity Contextじゃないとダメな場合もあれば、Application Contextの方が適切な場合もあり、はたまたどっち渡しても問題ない場合だってあります。・・・なんでこんなややこしいことになってるんでしょうね?
とりあえず、動けば正義でトライアンドエラーを繰り返してAndroidに慣れる。慣れてきたらソースコードを読んでみて、Contextがどう使われているかを気にしてみる。そうやってステップアップしていくしかありません。
長くなったので最後にまとめ。
私はActivity Contextを基本的に使うようにしていて、利用先でContextへの参照を持つ場合にはApplication Contextの利用を考える、というような感じでやってます。
Activity Context使っているのはThemeのためとかじゃなくて、単にgetApplicationContext()
を書くと長くなるからとかそんな理由です。明らかにActivityのライフサイクルより長生きするオブジェクトに対しては、Application Context渡していますけれども。
正直な所、メモリリークをおそれてContextにどれを使うか悩むより、メモリリークの確認の仕方を調べて、リークしていないかを確認することに力入れたほうが建設的なんじゃないかなと思います。
何を使うのが適切か迷ってまごまごする前に、どう使われているのかをまず確認する。Contextの使い分けに関してはこれが私の指針なんですが、そんなに大きく間違ってはないですよね?