2015年10月29日木曜日

【Android】メモリリークを防ぎたい 弱参照(WeakReference)を使おう

最近やっていたAndroid案件で、メモリリーク対策についてまた理解が進んだのでメモ。

主要な参考先:
http://tomokey.blogspot.jp/2011/05/android.html


コツ:
■内部クラスは static にする。
■「弱参照」という仕組みについて理解して、アクティビティやフラグメントといったライフサイクルのあるインスタンスを他のクラスに保持させるときはこの弱参照を使う。
■内部クラスから外側クラスにアクセスする必要がある場合、内部クラスのプロパティに外側クラスのインスタンスを持たせて、内部クラスのコンストラクタで外側クラスのインスタンスを渡す。この時、弱参照インスタンスとして保持しておく。
■処理の効率化のために作成した色々なクラスでも、プロパティにアクティビティやフラグメントを保持するときは弱参照経由にしておく。
■あとは、とにかくフラグメントやアクティビティを遷移するときは古いインスタンスを初期化していく。onResume()で準備したインスタンスはonPause()で片付けて、onStart()で準備したインスタンスはonStop()で片付ける。


---------------------------------
弱参照について。
通常、あるインスタンスへの参照を作成すると、それは「強参照」という強い参照になるらしい。
弱参照というのは弱い参照で、ガベージコレクションの対象になりやすい。
あるインスタンスについて、参照がひとつでも残っているとガベージコレクションの対象にならないという。
しかし、あるインスタンスについて、そこに残っている参照が弱参照のみだったら、それはGCの対象になるという。

アクティビティやフラグメントなど、ライフサイクルを持つものは、開発者が意図してないタイミングで破棄されたり再生成されたりするらしい。
しかしそうやってこっそり破棄された時、そのアクティビティやフラグメントへの参照(強参照)が残っていると、きちんと破棄されないでメモリを圧迫するという。
なので、これらへの参照は弱参照がいいのだということだ。

例:フラグメントクラスでアクティビティのインスタンスを持っておこうという時
//メンバ変数
private Activity activity;

//onStart() あたりで
× this.activity = getActivity();
○ this.activity = new WeakReference<Activity>(getActivity()).get();

弱参照とは、参照インスタンスである。
WeakReference クラスのインスタンス。
new WeakReference<型指定>(参照されるインスタンス) というコンストラクタで作成する。
このインスタンス自体は参照であってお目当てのインスタンスではない。
get() メソッドによって参照されているインスタンスを取り出す。


---------------------------------
インナークラスを持つフラグメントの見本
---------------------------------


public class MyFragment extends AbsFragmentWithViewFlipper{

    ///////////////////////////////////////////////////////////
    //フィールド
    ///////////////////////////////////////////////////////////

    //ボタン
    private Button btn;

    //ボタンのクリックリスナー
    private MyOnClickListener listener;


    ///////////////////////////////////////////////////////////
    /**
     * ライフサイクル:onStart()
     * フラグメントがユーザーに見えるように生成された
     * タイミングで呼び出される。
     *
     * アニメーションの開始など、表示に関わる初期化処理を行う。
     */
    @Override
    public void onStart(){
        super.onStart();

        //ボタン
        this.btn = (Button)getActivity().findViewById(R.id.btn);

        //クリックリスナー作成
        this.listener = new MyOnClickListener(this);

        //クリックリスナーをセット
        this.btn.setOnClickListener(listener);
    }//function


    ///////////////////////////////////////////////////////////
    //クラス内クラス
    ///////////////////////////////////////////////////////////
    /**
     * スイッチ操作時のリスナー
     */
    private static class MyOnClickListener implements OnClickListener{
        //外側クラスのインスタンス
        private MyFragment parent;

        //////////////////////////////
        /**
         * コンストラクタ
         */
        public MyOnClickListener(MyFragment fragment){
            //弱参照経由で外側クラスを受け取る
            parent = new WeakReference<MyFragment>(fragment).get();
        }//function

        //////////////////////////////
        /**
         * クリック時
         */
        @Override
        public void onClick(
            View view
        ){
            //外側クラスにアクセスする時は「parent」を使う

        }//function
    }//class
}//class