【面試】Handler/Runnable造成的內存泄漏

1 內存泄漏根本原因

內存泄漏的根本原因是:長生命週期的對象持有短生命週期的對象,短生命週期的對象就無法及時釋放。

2 Handler錯誤用法

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Toast.makeText(MainActivity.this, "測試", Toast.LENGTH_SHORT).show();
        }
    };

Handler允許我們發送延時消息,如果在延時消息未處理完,而此時Handler所在的Activity被關閉,但因爲上述Handler用法則可能會導致內存泄漏。首先,Handler是以內部類的形式被創建,那麼它將隱性持有外部類的對象,而且,在Toast中還以MainActivity.this方式顯性持有了MainActivity的對象,那麼,在延時消息未處理完時,Handler無法釋放外部類MainActivity的對象,從而導致內存泄漏產生。

3 Handler正確用法

package com.yds.jianshu.mobile;

import android.app.Activity;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.lang.ref.WeakReference;

public class MainActivity extends AppCompatActivity{
    private static final String TAG = "[MainActivity]";
    private MyHandler handler;
    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();

    }
    private void initData(){
        tv = findViewById(R.id.text);
        handler = new MyHandler(this);
        
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (handler!=null){
            handler.removeCallbacksAndMessages(null);
        }
    }


    private static class MyHandler extends Handler{
        private WeakReference<MainActivity>reference;
        public MyHandler(MainActivity activity){
            reference = new WeakReference<>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            MainActivity activity = reference.get();
            if (activity!=null){
                Toast.makeText(activity, "測試", Toast.LENGTH_SHORT).show();
                activity.tv.setText("測試");
            }
           
        }
    }
}


4 Runnable錯誤用法

    private Runnable mRunnable = new Runnable() {
        @Override
        public void run() {

        }
    };

一樣的道理,該使用方式是創建了一個內部類,內部類隱性持有外部類對象的引用,如果Activity結束,Runnable裏面的任務沒有處理完,則不會釋放Activity的引用,則Activity無法被回收,造成內存泄漏。

5 Runnable正確用法

    private static class MyRunnable implements Runnable{
        WeakReference<MainActivity> reference;
        public MyRunnable(MainActivity activity){
            reference = new WeakReference<>(activity);
        }
        @Override
        public void run() {
            MainActivity activity = reference.get();
        }
    }

6 總結

  • 如果直接new一個Handler,則Handler是一個非靜態內部類,它隱性地持有外部類的對象。如果外部類需要結束,但消息隊列中還有消息未處理完,則Handler不會釋放外部類的對象,從而造成內存泄漏。
  • 如果直接new一個Runnable,Runnable也是一個非靜態內部類,它隱性地持有外部類的對象。如果外部類需要結束,但Runnable中還有任務未處理完,則Runnable不會釋放外部類的對象,從而造成內存泄漏。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章