

Android の `Context` について教えて下さい。

引数などとして渡す `Context` ですが `this` を使うのではなく`getApplicationContext()`
を推奨されているブログを以前見ました。

`this` を使うとメモリを解放しても参照先が残るので、蓄積するとメモリリークになる可能性があるので `getApplicationContext()`
を使うべきだとのこと。

そこで質問ですが参照先が残るという挙動を確認する方法ってあるのでしょうか？


CC BY-SA 3.0
2016-11-22T06:31:51.790
30592
2016-11-23T21:46:19.943
2016-11-22T15:41:14.330
208
10206
question
android java android-studio
参照先が残るのを確認したい


まず、 `Context` が必要な状況で`this` の代わりに `getApplicationContext()`
を使うべきというのは常に正しいわけではありません。以前私が見たブログと同じだと思いますが、そのブログを見たといって間違った `Context`
の使い方をしている例を何度も見ています。

具体的には、`View`に絡むような操作に使用する場合、例えば `this`
が`Activity`であればその`Activity`に設定されているテーマの情報が反映された`View`が作られます。`Toast`に使う場合なども同様です。

質問にある参照先が残っているかどうかですが、Javaなので開放されるかどうかはGCの結果で決まります。なので、"メモリを解放しても"
という表現は正しくはないですが(明示的に開放することはできない)、"コード上すべての参照をクリアした状態" という意味でとらえると
`WeakReference` を使うのが簡単だと思います。

`Context`を使う際に併せて `WeakReference ref = new WeakReference(this);`
としてrefを作り、これをthis でないどこかに保持しておきます。そこでもたせた`this`が開放されているはずと思える部分で `System.gc()`
した上で `ref.get()`をしてみて、それが `null`であればどこにもそのインスタンスを参照しているコードがなかったことを意味します。

他にはデバッグ用のツールで確認する方法などもありますが説明が面倒なのでもし必要であれば再度質問して下さい。


CC BY-SA 3.0
2016-11-23T01:30:17.533
30608
2016-11-23T01:30:17.533
319
30592
answer


@zaki50 さんのと考え方は同じですが、別解です。

@zaki50 さんのやりかただと、`ref.get()`がリファレントの実体(i.e.
`Context`インスタンス)を返すため、コードの書き方によってはGCを邪魔してしまいます。また、GCを待つために何度も`ref.get()`を実行する必要が出てくるかもしれません。

これらをさけるために`ReferenceQueue`が使えます。これに`WeakReference`を登録しておくと、そのリファレントへの参照がなくなったときに`ReferenceQueue`を通して通知を受け取ることができます。

まず、`Context`インスタンスを使う辺りで、それを以下の様に`ReferenceQueue`に登録します。

[code]

    ReferenceQueue<Context> queue = new ReferenceQueue<>();
    WeakReference ref = new WeakReference<>(this, queue);
    
[/code]

これらの`queue`と`ref`は、後で参照できてGCされないどこかに保持しておきます。  
次に、登録したインスタンスへの参照がなくなったと思うところで、以下を実行し、本当になくなったことを確認できます。

[code]

    System.gc();
    queue.remove();
    System.out.println("参照なくなった");
    
[/code]

登録したインスタンスへの参照がなくなったことにガーベジコレクタが気付くと、ガーベジコレクタはそのインスタンスの`WeakReference`を`queue`に突っ込みます。  
`queue.remove()`は`queue`に`WeakReference`が突っ込まれるまでブロックするので、ループ等しなくてもGCを待てます。  
`queue.remove()`が返すのは登録したインスタンスそのものではなく、その`WeakReference`なので、GCのじゃまも一切しません。


CC BY-SA 3.0
2016-11-23T21:37:40.877
30623
2016-11-23T21:46:19.943
2016-11-23T21:46:19.943
19437
19437
30592
answer