Android進階知識:Handler相關

一、前言

HandlerAndroid中的地位沒必要說了,學習安卓開發必然跳不過Handler,講解Handler的文章也是很是的多,這裏我對我所瞭解的Handler這種Android中多線程間的通訊方式的相關知識作一個總結。面試

二、Handler使用

Handler做爲線程間通訊的方式,最常使用的地方就是子線程更新UI。由於AndroidUI控件不是線程安全的,若是在多線程下併發訪問可能會致使UI控件處於不可預期的狀態。因此在子線程想要更新UI的時候會使用handler.sendMessage(Message msg)方法通知主線程更新。
關於Handler的經常使用方法,除了sendMessage系列方法,handler還有一個post系列方法,能夠在子線程中經過handler.post(Runnable r)方法進行一些在主線程的操做。
sendMessage系列方法:安全

public final boolean sendMessage(Message msg)
public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public final boolean sendMessageAtFrontOfQueue(Message msg)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
複製代碼

post系列方法:bash

public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
public final boolean postDelayed(Runnable r, Object token, long delayMillis)
public final boolean postAtFrontOfQueue(Runnable r)
複製代碼

簡單運用:網絡

private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_SEND:
                    mTitleText.setText((String) msg.obj);
                    break;
            }
        }
    };
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                           //子線程耗時操做
                          Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                mTitleText.setText("post");
                            }
                        });
                    }
                }).start();
                break;
            case R.id.button2:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            //子線程耗時操做
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //發送消息
                        Message message = Message.obtain();
                        message.what = MESSAGE_SEND;
                        message.obj = "sendMessage";
                        handler.sendMessage(message);
                    }
                }).start();
                break;
        }
    }
複製代碼

三、Handler源碼閱讀

學會使用後下一步就是學習原理 ,學習原理就免不了要閱讀源碼。 從上面的圖已經能看出 Handler的基本工做流程。過程當中主要涉及瞭如下四個類:

  • Handler
  • Looper
  • MesageQueue
  • Message

接下來首先就從Handler看起。多線程

Handler的構造方法:

使用Handler第一步就是建立一個Handler對象,從而首先調用的就是Handler的構造方法。固然Handler構造方法有不少的不一樣參數的重載,這裏只看最主要的兩個。併發

public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
 public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
        //獲取當前線程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //給Handler中的成員變量初始化
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
複製代碼

第一個是傳參帶有Looper的所調用的方法,其中就是作了些初始化的操做,調用這個方法建立的HandlerLooper就是做爲參數傳入的Looper
第二個構造方法中第一個if判斷當前Handler類是否有內存泄漏的隱患,主要是檢驗當前類是不是匿名類、成員類、局部類是否靜態修飾等。接着經過Looper.myLooper()方法獲取當前線程中的Looper對象。接下來判斷若是這個Looper爲空,說明當前Handler初始化所在線程沒有Looper,會拋出Exception。這裏就決定了Handler初始化所在線程必須有Looper,因此在子線程中建立Handler以前先要經過Looper.prepare()方法建立Looper。接着就是對Handler中一些成員變量進行初始化,將Looper中的消息隊列引用傳遞給mQueue,將構造中傳入的callback初始化等。
來看下LoopermyLooper()方法:app

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
複製代碼

這裏是從ThreadLocal中獲取到一個Looper。關於ThreadLocal的相關知識點能夠看Android進階知識:ThreadLocalless

Handler建立以後,在須要進行主線程操做的時候,咱們會使用handlersendMessage系列方法,或者post系列方法。這裏一樣有不少重載,具體的方法在前文中已經列舉。這裏先看post方法:異步

post系列方法:
public final boolean post(Runnable r){
    return  sendMessageDelayed(getPostMessage(r), 0);
 }
複製代碼

能夠看到無論哪一個post方法中,都是經過getPostMessage()方法構建一個Message最終仍是調用對應的sendMessage方法。async

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
複製代碼

getPostMessage方法中經過Message.obtain()獲取一個Message將傳入的Runnable賦給Message中的callback,接着返回這個Message

sendMessage系列方法:

由於post方法的最後又都調用了對應的sendMessage方法,因此接下來看sendMessage方法的實現:

public final boolean sendMessage(Message msg)
 {
    return sendMessageDelayed(msg, 0);
 }
 
 public final boolean sendMessageDelayed(Message msg, long delayMillis)
 {
   if (delayMillis < 0) {
       delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
 }
 
 public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
    
複製代碼

這裏看到通過層層調用最終執行了enqueueMessage方法。這裏要注意的是sendMessageDelayed方法中設置的延遲時間是經過SystemClock.uptimeMillis()+ 延遲時間來計算的。
SystemClock.uptimeMillis()方法是獲取從開機到如今的毫秒數,與System.currentTimeMillis()獲取從1970年1月1日到如今的毫秒數不一樣,後者會受到手機系統時間影響,而系統時間能夠手動修改。sendMessageAtTime方法中對MessageQueue進行是否爲null的判斷,爲null拋出異常,這裏的MessageQueue就是在Handler構造函數中Looper中的Queue

消息入隊:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
    }

複製代碼

enqueueMessage方法中經過msg.target=this這一句,將Handler的引用傳遞給了Message中的target。接着調用了MessageQueueenqueueMessage方法。接下來進入MessageQueue

boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; } 複製代碼

MessageQueueenqueueMessage方法中,傳入的msg就是要插入到消息隊列的新消息,when是這個任務的延時時間。方法裏首先對消息裏的target也就是Handler進行空判斷,爲空拋出異常。接着判斷了這個消息是否被使用和消息隊列是否退出等。看到msg.when = when;這一行將延遲時間傳遞保存到消息內部,下一行定義了一個臨時指針p用來指向mMessage,這個mMessage就是消息隊列的首結點,接下來的這個if-else作的就是將新消息根據他的when大小,將他按順序加入到隊列中合適位置上。這裏能夠看出這個消息隊列其實是個鏈表,每一個Message是一個結點,結點中有一個next指針存放下一個結點位置。這裏的新消息的添加,就是向這個鏈表中插入一個節點。
先看if中判斷,p==null即首結點爲nullwhen=0及延時時間爲0,當即執行,when<p.when即新節點的延時時間小於當前鏈表首結點的延時時間,這三種狀況下直接將新消息節點插到鏈表頭部,即msg.next=p;mMessages=msg這兩行的操做,而後喚醒消息線程處理新消息。else就要將新節點根據when的大小插入到鏈表中合適位置,這裏又定義了一個臨時指針prev,指向p指向的前一個節點,看到for (;;)循環中,將p指針不斷向後移,直到p等於null即鏈表結尾或者新結點的when<p.when的時候,即這個鏈表是按照節點when的從小到大的順序排列插入的。此時break出循環,將新節點插入到此處,即msg.next=p;prev.next=msg。到此消息發送加入消息隊列的過程節結束了。

取出處理消息:

有往消息隊列里加消息,就有從消息隊列取消息。誰來取呢?就是Looper,以前看到在Handler的構造方法裏,經過Looper.myLooper()方法獲取到當前線程(handler建立所在線程)的Looper對象。並且還知道了建立Handler的線程必須存在一個Looper對象不然會拋出異常。這也是咱們不能在子線程裏直接建立使用Handler的緣由。那麼爲何主線程能夠直接建立Handler呢?是由於主線程中有Looper。那麼主線程的Looper又是哪來的呢?這須要看到ActivityThread類裏的代碼。

public static void main(String[] args) {
        ......
       
        Looper.prepareMainLooper();

        ......
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

       ......
    }
複製代碼

ActivityThreadmain方法在app啓動時調用,這裏省去了一些無關代碼,只看與主線程Looper相關的。能夠看到在main方法裏調用了Looper.prepareMainLooper()方法,以後獲取了一個主線程的Handler,接着調用了Looper.loop()方法。一個方法一個方法來看,先是prepareMainLooper()方法:

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
複製代碼

prepareMainLooper()方法中調了prepare(false)方法,這裏quitAllowed傳的是false,這個標記會傳遞到MessageQueue中,這裏說明主線程的消息隊列是不容許退出的。prepare()方法裏初始化了new了一個Looper對象,並將它添加到當前線程的ThreadLocal中。接着到Looper的構造函數中看看:

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
複製代碼

Looper的構造函數很簡單,建立了MessageQueue消息隊列保存了一個當前線程的對象。從這裏看出來建立一個Handler須要傳入建立線程的Looper,而建立Looper又對應建立了一個MessageQueue。下面回到main方法中看thread.getHandler()這個獲取主線程Handler方法:

final Handler getHandler() {
        return mH;
    }
複製代碼

這裏直接返回mH這個Handler,那麼這個Handler是在哪裏建立的呢?

final H mH = new H();
複製代碼

跟蹤下去發現這個mHActivityThread類的成員變量,而且直接初始化。因此這個Handler就是在main方法中建立ActivityThread對象時就初始化了。最後調用Looper.loop()方法開始循環取消息:

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
        ......
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ......
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ......            
        }
    }
複製代碼

這裏一樣省略了部分代碼,來看主要的流程,首先仍是獲取當前線程Looper對空作了校驗。而後從Looper中獲取到MessageQueue,接着進入for循環,調用queue.next()從消息隊列中取出消息,根據註釋,這個方法有可能會阻塞,若是返回的msg爲null,說明消息隊列正在退出。接着在try代碼塊中調用了msg.target.dispatchMessage(msg)方法,這個msg.target就是在前面enqueueMessage方法中設置的發送消息的Handler,因此這裏調用了Handler的dispatchMessage(msg)方法。

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
複製代碼

dispatchMessage方法中對callback進行了判斷,這裏有兩個callback,msg.callback是對應Handler中的post方法,將傳入的Runnable存入Messagecallback中,若是調用post方法msg.callback不爲空調用handleCallback方法,最終會執行Runnablerun方法,開始執行post時傳進來的任務。

private static void handleCallback(Message message) {
        message.callback.run();
    }
複製代碼

第二個mCallback,對應的是建立Handler時的傳參,若是不爲空會執行mCallback.handleMessage方法。若是初始化時沒傳mCallback,就會執行handleMessage(msg)方法:

/**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
複製代碼

這個方法就是咱們本身須要實現的處理消息的方法,也是咱們最經常使用重寫的方法。至此UI主線程中建立HandlerLooper,而且Looper開啓輪詢到調用了HandlerdispatchMessage處理消息的過程就結束了。

子線程Handler使用:

回到上面說的,這是主線程中HandlerLooper的初始化,那麼要在子線程使用Handler該怎麼作呢?

new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler handler = new Handler(Looper.myLooper()) {
                    @Override
                    public void handleMessage(Message msg) {
                        //處理消息
                    }
                };
                Looper.loop();
            }
        }).start();
複製代碼

其實和主線程同樣,由於子線程中沒有Looper因此須要咱們本身建立Looper而且調用Looper.loop()方法開始輪詢。這裏的Looper.prepare()方法和prepareMainLooper()方法同樣最終會調用prepare(boolean quitAllowed)方法,這時傳入的quitAllowed爲true,表示消息隊列能夠退出。
至此Handler機制相關類HandlerLooperMessageQueue的主要方法源碼都看完了,他們之間的工做流程相互關係也都清楚了。

Message類:

其實還剩一個Message消息類,Message類中主要看一個obtain()方法,Message除了能夠經過new來建立,還能夠經過obtain()方法來得到,而且obtain()方法是從全局池中獲取Message對象,能避免從新分配對象。

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
複製代碼

這裏看到只要sPool不等於null,就從sPool頭上去一個消息節點返回。因此使用obtain方法,相對直接new一個Message能減小內存分配。

四、Handler相關面試題

Q1:Handler機制中涉及到了哪些類?做用分別是什麼?

A1:主要涉及到HandlerLooperMessageQueueMessage這四個類。
Handler:發送消息到消息隊列。
Looper:從消息隊列中取出消息,交給Handler的dispatchMessage方法處理。
MessageQueue:消息隊列存儲管理消息插入取出。
Message:消息類,攜帶着消息數據。

Q2:MessageQueue 中的 Message有順序嗎?若是有是按什麼順序排列的?

A2:經過以前的源碼閱讀知道,是有順序的,是根據Message.when這個相對時間排列的。

Q3:子線程中能夠建立 Handler 對象嗎?

A3:一樣從源碼中能夠知道,子線程中不能直接建立HandlerHandler建立須要指定一個Looper,子線程中沒有Looper,須要先建立Looper,調用Looper.loop方法。

Q4:MessageQueue內部實現是一個隊列嗎?

A4:不是,內部實現實際上是一個單鏈表。

Q5:Looper的quit方法和quitSafely方法有什麼區別?

A5:quit方法會清空消息隊列中的全部消息,quitSafely方法只會清除全部延遲消息,非延遲消息仍是分發出去交給Handler處理。具體仍是看源碼:

public void quit() {
        mQueue.quit(false);
    }

    public void quitSafely() {
        mQueue.quit(true);
    }
複製代碼

這裏其實是調用了MessageQueuequit方法:

void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;
            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }
            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

複製代碼

quit方法傳入的是false調用的是removeAllMessagesLocked()方法,quitSafely傳入的是true調用的是removeAllFutureMessagesLocked方法。

private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }
複製代碼

removeAllMessagesLocked方法中直接將全部消息清空。

private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }
複製代碼

removeAllFutureMessagesLocked方法中先作判斷若是首節點when大於當前時間說明全是延遲消息,就一樣調用removeAllMessagesLocked處理所有清空,不然循環找到隊列中when大於now也就是大於當前時間的節點位置,將該節點消息同其後的全部消息清空。

Q6:爲何主線程不會由於Looper.loop()裏的死循環卡死?

A6:這個涉及到LinuxEpoll機制。簡單來講就是Android應用程序的主線程在進入消息循環過程前,會在內部建立一個Linux管道(Pipe),這個管道的做用是使得Android應用程序主線程在消息隊列爲空時能夠進入空閒等待狀態,而且使得當應用程序的消息隊列有消息須要處理時喚醒應用程序的主線程。
具體解釋:Android中爲何主線程不會由於Looper.loop()裏的死循環卡死?

五、Handler引起內存泄漏

Handler致使的內存泄露,是平時寫代碼不注意很是容易出現的問題,並且內存泄露多了對應用性能影響較大,因此單獨研究下。
通常咱們使用Handler更新UI都是這樣的:

public class HandlerActivity extends Activity {
    private TextView mTextView;
    private final int MESSAGE_SEND = 0x01;
    private MyHandler handler = new MyHandler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        mTextView = findViewById(R.id.textView);
        Message obtain = Message.obtain();
        obtain.what = MESSAGE_SEND;
        obtain.obj = "文字";
        handler.sendMessageDelayed(obtain, 1000 * 60 * 10);
    }

    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MESSAGE_SEND) {
                mTextView.setText((String) msg.obj);
            }
        }
    }
}

複製代碼

這裏看到進入Activity就發送了一個延遲消息,現實開發中多是網絡有延遲又或者進入一個界面後馬上離開這時數據還沒加載好,只要是耗時任務尚未完成,當前的Activity又須要銷燬,這時候由於此時MyHander,它隱式持有外部類的引用,當Activity銷燬時,此時異步耗時任務尚未結束,仍然持有Activity的引用,使得Activity沒法回收,形成內存泄漏。 經過集成LeakCanary能夠檢測到內存泄漏,以下圖:

LeakCanary
一樣經過 AndroidStudio Profiler能夠查看到內存泄露:
經過屢次打開關閉 HandlerActivity,而後觀察內存狀況,能夠發現即便在我手動 GC屢次後,仍然存在多個實例沒有被回收的現象。

內存泄漏解決方法:

  1. Handler定義爲靜態,靜態內部類不會持有外部類的引用。
  2. 由於靜態內部類不持有外部類引用,因此在Handler中沒法訪問外部類的成員,須要用一個外部類的弱引用來訪問外部成員,又由於是弱引用,在GC時能夠將其回收,不會形成內存泄露。
  3. ActivityonDestory方法中調用removeCallbacksAndMessages方法清除消息隊列。

解決內存泄漏:

public class WeakHandlerActivity extends Activity {
    private TextView mTextView;
    private static final int MESSAGE_SEND = 1;
    private MyHandler handler = new MyHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        mTextView = findViewById(R.id.textView);
        Message obtain = Message.obtain();
        obtain.what = MESSAGE_SEND;
        obtain.obj = "文字";
        handler.sendMessageDelayed(obtain, 1000 * 60 * 10);
    }
   //靜態內部類
    static class MyHandler extends Handler {
        private final WeakReference<WeakHandlerActivity> mActivty;
        public MyHandler(WeakHandlerActivity activity) {
        //初始化Activity的弱引用
            mActivty = new WeakReference<WeakHandlerActivity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            WeakHandlerActivity activity = mActivty.get();
            if (activity != null) {
                if (msg.what == MESSAGE_SEND) {
                    activity.mTextView.setText((String) msg.obj);
                }
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //清除消息隊列
        handler.removeCallbacksAndMessages(null);
    }
}
複製代碼

經過這三個方法結合使用,就能夠解決Handler致使的內存泄漏的問題。此次再經過Profiler來查看內存狀況:

能夠看到在屢次打開關閉界面後,仍然會存在多個 WeakHandlerActivity實例。
可是在 GC事後內存中的 WeakHandlerActivity已經被所有回收,不會繼續佔用內存,形成泄漏。

六、總結

  • Handler是Android提供的一種線程間通訊方式。由於Android中UI控件不是線程安全的,多線程併發訪問會出現同步問題,因此若是子線程想更新UI一般經過Handler來完成線程間通訊。

  • Handler的工做流程主要是由Handler發送消息,將消息添加到消息隊列MessageQueue中,再經過輪詢器Looper從消息隊列中取出消息,交給Handler去分發處理消息對應任務。

  • Handler使用時容易發生內存泄露,記得經過靜態內部類+弱引用的方式使用Handler,而且在Activity的onDestory方法裏記得清除消息隊列。

相關文章
相關標籤/搜索