1. 程式人生 > >Handler記憶體洩露分析與解決方案

Handler記憶體洩露分析與解決方案

一、記憶體洩露分析

內部類會有一個指向外部類的引用。

垃圾回收機制中約定,當記憶體中的一個物件的引用計數為0時,將會被回收。

Handler 作為 Android 上的非同步訊息處理機制(好吧,我大多用來進行 worker thread 與 UI 執行緒同步),它的工作是需要 Looper 和 MessageQueue 配合的。簡單的說,要維護一個迴圈體(Looper)處理訊息佇列(MessageQueue)。每迴圈一次就從 MessageQueue 中取出一個 Message,然後回撥相應的訊息處理函式。

如果,我是說如果,迴圈體中有訊息未處理(Message 排隊中),那麼 Handler 會一直存在,那麼 Handler 的外部類(通常是 Activity)的引用計數一直不會是0,所以那個外部類就不能被垃圾回收。很多人會遇到 activity的onDestroy方法一直不執行就是這個原因。

一旦 Handler 被宣告為內部類,那麼可能導致它的外部類不能夠被垃圾回收。如果 Handler 是在其他執行緒(我們通常成為 worker thread)使用 Looper 或 MessageQueue(訊息佇列),而不是 main 執行緒(UI 執行緒),那麼就沒有這個問題。如果 Handler 使用 Looper 或 MessageQueue 在主執行緒(main thread),你需要對 Handler 的宣告做如下修改:
宣告 Handler 為 static 類;在外部類中例項化一個外部類的 WeakReference(弱引用)並且在 Handler 初始化時傳入這個物件給你的 Handler;將所有引用的外部類成員使用 WeakReference 物件。

二、解決方案

解決方法(一)

宣告 Handler 為 static 類;在外部類中例項化一個外部類的 WeakReference(弱引用)並且在 Handler 初始化時傳入這個物件給你的 Handler;將所有引用的外部類成員使用 WeakReference 物件。

public class MethodSolveOne extends AppCompatActivity {

    private static final String TAG = "MethodSolveOne";


    private CopyFileHandler mHandler;
    private TextView mDisplayTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.method_solve_one);
        mDisplayTextView = (TextView)findViewById(R.id.display_text_view);
        mHandler = new CopyFileHandler(this);
        startCopyFileThread();
    }

    private void startCopyFileThread() {
        Log.d(TAG, "startCopyFileThread");
        new Thread(new Runnable() {
            @Override
            public void run() {
                //DO SOMETHING LIKE: copyDBFile();
                Message msg = mHandler.obtainMessage();
                msg.what = 1;
                mHandler.sendMessage(msg);
            }
        }).start();
    }

    private void changeDisplayTextView(String str){
        mDisplayTextView.setText(str);
    }
    private static class CopyFileHandler extends Handler {
        WeakReference<MethodSolveOne> mActivity;

        public CopyFileHandler(MethodSolveOne activity) {
            mActivity = new WeakReference<>(activity);
        }

        public void handleMessage(Message msg) {
            final MethodSolveOne activity = mActivity.get();
            switch (msg.what){
                case 1:
                    activity.changeDisplayTextView("I have changed!!!");
            }
        }
    }
}

解決方法(二)

在worker thread 中使用 Looper 或 MessageQueue

private Handler testHandler;
private Thread mThread = new Thread() {
    public void run() {
        Log.d(TAG, "mThread run");
        Looper.prepare();
        testHandler = new Handler() {
            public void handleMessage(Message msg) {
                Log.d("TAG", "worker thread:" + Thread.currentThread().getName());
                switch (msg.what) {
                    //handle message here
                }
            }
        };
        Looper.loop();
    }
};
//start thread here
if(Thread.State.NEW==mThread.getState())

{
    Log.d(TAG, "mThread name: " + mThread.getName());
    mThread.start();
}
//send message here
testHandler.sendEmptyMessage(1);