Android內存泄露之Handler

axdh4644 8年前發布 | 6K 次閱讀 Android開發 移動開發

Thread 內存泄露

  線程也是造成內存泄露的一個重要的源頭。線程產生內存泄露的主要原因在于線程生命周期的不可控。

1.看一下下面是否存在問題

/**
 *
 * @version 1.0.0
 * @author Abay Zhuang <br/>
 *         Create at 2014-7-17
 */
public class ThreadActivity extends Activity {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new MyThread().start();
    }
    private class MyThread extends Thread {
        @Override
        public void run() {
            super.run();
            dosomthing();
        }
    }
    private void dosomthing(){

    }
}

是否您以前也是這樣用的呢。

沒有問題?

Eclipse 工具有這樣的警告提示 警告:

This Handler class should be static or leaks might occur
(com.example.ta.HandlerActivity.1) 意思:class 使用靜態聲明否者可能出現內存泄露。

為啥出現這樣的問題呢

Handler 的生命周期與Activity 不一致

  • 當Android應用啟動的時候,會先創建一個UI主線程的Looper對象,Looper實現了一個簡單的消息隊列,一個一個的處理里面的Message對象。主線程Looper對象在整個應用生命周期中存在。

  • 當在主線程中初始化Handler時,該Handler和Looper的消息隊列關聯(沒有關聯會報錯的)。發送到消息隊列的Message會引用發送該消息的Handler對象,這樣系統可以調用 Handler#handleMessage(Message) 來分發處理該消息。

handler 引用 Activity 阻止了GC對Acivity的回收

  • 在Java中,非靜態(匿名)內部類會默認隱性引用外部類對象。而靜態內部類不會引用外部類對象。

  • 如果外部類是Activity,則會引起Activity泄露 。

    當Activity finish后,延時消息會繼續存在主線程消息隊列中1分鐘,然后處理消息。而該消息引用了Activity的Handler對象,然后這個Handler又引用了這個Activity。這些引用對象會保持到該消息被處理完,這樣就導致該Activity對象無法被回收,從而導致了上面說的 Activity泄露。

如何避免?

  • 使用顯形的引用,1.靜態內部類。 2. 外部類

  • 使用弱引用 2. WeakReference

  修改代碼如下:

/**
 *
 * 實現的主要功能。
 *
 * @version 1.0.0
 * @author Abay Zhuang <br/>
 *         Create at 2014-7-28
 */
public class HandlerActivity2 extends Activity {
    private static final int MESSAGE_1 = 1;
    private static final int MESSAGE_2 = 2;
    private static final int MESSAGE_3 = 3;
    private final Handler mHandler = new MyHandler(this);
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler.sendMessageDelayed(Message.obtain(), 60000);
        // just finish this activity
        finish();
    }
    public void todo() {
    };
    private static class MyHandler extends Handler {
        private final WeakReference<HandlerActivity2> mActivity;
        public MyHandler(HandlerActivity2 activity) {
            mActivity = new WeakReference<HandlerActivity2>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            System.out.println(msg);
            if (mActivity.get() == null) {
                return;
            }
            mActivity.get().todo();
        }
    }

上面這樣就可以了嗎?

當Activity finish后 handler對象還是在Message中排隊。 還是會處理消息,這些處理有必要?  正常Activitiy finish后,已經沒有必要對消息處理,那需要怎么做呢?

解決方案也很簡單,在Activity onStop或者onDestroy的時候,取消掉該Handler對象的Message和Runnable。  通過查看Handler的API,它有幾個方法:

removeCallbacks(Runnable r)和removeMessages(int what)等。

代碼如下:

/**
 * 一切都是為了不要讓mHandler拖泥帶水
 */
@Override
public void onDestroy() {
    mHandler.removeMessages(MESSAGE_1);
    mHandler.removeMessages(MESSAGE_2);
    mHandler.removeMessages(MESSAGE_3);
    // ... ...
    mHandler.removeCallbacks(mRunnable);
    // ... ...
}

如果上面覺的麻煩,也可以如下面:

@Override
public void onDestroy() {
    //  If null, all callbacks and messages will be removed.
    mHandler.removeCallbacksAndMessages(null);
}

 

 本文由用戶 axdh4644 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!