Androidでよく見られるいくつかのメモリリーク-Handler

4931 ワード

Handlerの使用によるメモリリークの問題は最も一般的であると言える.通常、ネットワークタスクの処理や要求コールバックのカプセル化などのapiはHandlerによって処理されるべきである.Handlerの使用コードの作成に規範がないと、メモリリークの可能性がある.以下の例である.
public class MainActivity extends AppCompatActivity {
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            //...
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        loadData();
    }
    private void loadData(){
        //...request
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }
}

このようにHandlerを作成するとメモリが漏洩し、mHandlerはHandlerの匿名の内部クラスのインスタンスであるため、外部クラスActivityの参照を持っており、メッセージキューがLooperスレッドで処理メッセージをポーリングし続けていることを知っています.このActivityが退くと、メッセージキューに未処理のメッセージがあるか、メッセージを処理しているか、メッセージメッセージメッセージはmHandlerインスタンスの参照を持ち、mHandlerはActivityの参照を持っているため、このActivityのメモリリソースがタイムリーに回収できず、メモリ漏洩を引き起こす.
では、どうすればこの問題を避けることができますか.ここでは2つの解決策を提供します.この問題は、Activityが破棄されたときに参照しているHandlerがまだ存在することに気づき、GCがActivityを回収できなくなったためである.最も簡単で最も直接的な方法は、ActivityのOnDestoryメソッドですべてのメッセージとコールバックである空のキューを削除することであり、MessageQueueが解放されてHandlerが解放され、GCは破壊されたActivityを正常に回収することができる.このソリューションの方法は次のとおりです.
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }

このソリューションでは、メッセージキューの役割を無視します.HandlerのActivityへの参照によってGCがActivityを回収できなくなったため、私たちは直接暴力的にメッセージキューを空にしました.もしこれらのメッセージキューの中のメッセージを処理する必要があるならば?これは、弱い参照という2つ目のソリューションを使用する必要があります.その原理はhandlerで使用されているすべてのオブジェクトを弱引用にすることであり、Androidでメモリを回収できるときに、直接回収できるようにすることを目的としています.
public class MainActivity extends AppCompatActivity {
    private MyHandler mHandler = new MyHandler(this);
    private TextView mTextView ;
    private static class MyHandler extends Handler {
        private WeakReference reference;
        public MyHandler(Context context) {
            reference = new WeakReference<>(context);
        }
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = (MainActivity) reference.get();
            if(activity != null){
                activity.mTextView.setText("");
            }
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView)findViewById(R.id.textview);
        loadData();
    }
    private void loadData() {
        //...request
        Message message = Message.obtain();
        mHandler.sendMessage(message);
    }
}