AsyncTaskをベースとするクラスで、doInBackgroundとonPostExecuteメソッドに非同期処理本体、完了時の処理を実装し、インスタンス化してexecuteメソッドを実行すると非同期処理がキックされる。ネットワークを使う処理はUIスレッドでは実行できないので、AsyncTaskなどを使った非同期処理が必須。
無名クラスを定義しているクラスのオブジェクトのメンバにアクセスできるのは、明示的には書かないが、無名クラスのオブジェクトが、その外のクラスのオブジェクトへの参照を隠し(implicit)持っているから。これは実は注意が必要で、コンパイル時にも警告が出る。"This AsyncTask class should be static or leaks might occur."というあれだ。私のようなJava初心者には結構難しい警告である。
非同期実行しているライブな無名クラスのオブジェクトが、そのオブジェクトを作った元オブジェクト参照していることになるので、元オブジェクトが破棄されてよいオブジェクトであっても破棄されないことになる。メモリリークが起きるかもしれない、というわけ。
そんなことあるのかい? と思われるかもしれないが、Activity から非同期処理をキックしたと考えてみると良い。非同期処理の進行とは無関係にActivityは閉じられ破棄される。非同期処理が長く走る処理だと、AsyncTaskのオブジェクトがActivityを参照しているため、Activityがいつまで経っても破棄されないことになる。非同期処理が短時間の処理ならあまり神経質になる必要はないのかもしれない。
この関係を解消するやり方は、非同期処理用のクラスを外に作ること。非同期処理で元オブジェクトを参照しないなら、これで解決できる。しかし、多くの場合は、元オブジェクトを参照したくなるはず。だって、非同期処理は、キックされたところにある処理に関係が深いだろうから。
どうしても元オブジェクトへの参照が必要な場合は、WeakReferenceという仕掛けを使うと良いようだ、これも新たにクラスを作らなければならないのだが、元オブジェクトへの参照をWeakReferenceでラップして保持するようにする。実際にそれを参照する処理を実行するときには、ライブな参照を取り出せたときだけ実行する形になる。
ということで、コンパイラの警告は消せるのだが、よくよく考えてほしい。特に、元オブジェクトがActivity,の場合。イベントドリブンに処理が分解できていて、Obseverパターンがうまく実装できている場合、元Activityへの参照は消せる場合も多いはず。非同期処理でデータのリポジトリが更新され、そのリポジトリを見ているObserverが登録されていて、Observerパターンでビューが更新されるような形がきれいに出来上がっているなら、非同期処理の完了処理でActivityが直接参照されることはない。
どっちを取るかは、、、メンテの必要性かかっているのかなぁ。
0 件のコメント :
コメントを投稿