Android進階知識:Handler相關

1、前言

Handler在Android中的地位不必說了,學習安卓開發必然跳不過Handler,講解Handler的文章也是非常的多,這裏我對我所瞭解的Handler這種Android中多線程間的通信方式的相關知識做一個總結。

2、Handler使用

Handler作爲線程間通信的方式,最常使用的地方就是子線程更新UI。因爲Android的UI控件不是線程安全的,如果在多線程下併發訪問可能會導致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系列方法:

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;
        }
    }

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

  public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

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

Handler創建之後,在需要進行主線程操作的時候,我們會使用handler的sendMessage系列方法,或者post系列方法。這裏同樣有很多重載,具體的方法在前文中已經列舉。這裏先看post方法:

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

可以看到不管哪個post方法中,都是通過getPostMessage()方法構建一個Message最終還是調用對應的sendMessage方法。

  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。接着調用了MessageQueue的enqueueMessage方法。接下來進入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;
    }

MessageQueue的enqueueMessage方法中,傳入的msg就是要插入到消息隊列的新消息,when是這個任務的延時時間。方法裏首先對消息裏的target也就是Handler進行空判斷,爲空拋出異常。接着判斷了這個消息是否被使用和消息隊列是否退出等。看到 msg.when = when;這一行將延遲時間傳遞保存到消息內部,下一行定義了一個臨時指針p用來指向mMessage,這個mMessage就是消息隊列的首結點,接下來的這個if-else做的就是將新消息根據他的when大小,將他按順序加入到隊列中合適位置上。這裏可以看出這個消息隊列實際上是個鏈表,每個Message是一個結點,結點中有一個next指針存放下一個結點位置。這裏的新消息的添加,就是向這個鏈表中插入一個節點。
先看if中判斷,p==null即首結點爲null,when=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();

       ......
    }

ActivityThread中main方法在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();

跟蹤下去發現這個mH是ActivityThread類的成員變量,並且直接初始化。所以這個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存入Message的callback中,如果調用post方法msg.callback不爲空調用handleCallback方法,最終會執行Runnable的run方法,開始執行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主線程中創建Handler,Looper,並且Looper開啓輪詢到調用了Handler的dispatchMessage處理消息的過程就結束了。

子線程Handler使用:

回到上面說的,這是主線程中Handler、Looper的初始化,那麼要在子線程使用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機制相關類Handler、Looper、MessageQueue的主要方法源碼都看完了,他們之間的工作流程相互關係也都清楚了。

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能減少內存分配。

4、Handler相關面試題

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

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

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

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

Q3:子線程中可以創建 Handler 對象嗎?

A3:同樣從源碼中可以知道,子線程中不能直接創建Handler,Handler創建需要指定一個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);
    }

這裏實際上是調用了MessageQueue的quit方法:

 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:這個涉及到Linux的Epoll機制。簡單來說就是Android應用程序的主線程在進入消息循環過程前,會在內部創建一個Linux管道(Pipe),這個管道的作用是使得Android應用程序主線程在消息隊列爲空時可以進入空閒等待狀態,並且使得當應用程序的消息隊列有消息需要處理時喚醒應用程序的主線程。
具體解釋:Android中爲什麼主線程不會因爲Looper.loop()裏的死循環卡死?

5、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. 在Activity的onDestory方法中調用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已經被全部回收,不會繼續佔用內存,造成泄漏。

6、總結

  • Handler是Android提供的一種線程間通信方式。因爲Android中UI控件不是線程安全的,多線程併發訪問會出現同步問題,所以如果子線程想更新UI通常通過Handler來完成線程間通信。

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章