自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android進(jìn)階之Handle和Looper消息機(jī)制原理和源碼分析(不走彎路)

移動開發(fā) Android
App中一般多會有多個線程,多線程之間難免需要進(jìn)行通信。在我們平時開發(fā)中線程通信用的最多的就是Handler,例如子線程進(jìn)行數(shù)據(jù)處理,在主線程中進(jìn)行UI更新。

[[417195]]

本文轉(zhuǎn)載自微信公眾號「Android開發(fā)編程」,作者Android開發(fā)編程 。轉(zhuǎn)載本文請聯(lián)系A(chǔ)ndroid開發(fā)編程公眾號。

前言

App中一般多會有多個線程,多線程之間難免需要進(jìn)行通信。在我們平時開發(fā)中線程通信用的最多的就是Handler,例如子線程進(jìn)行數(shù)據(jù)處理,在主線程中進(jìn)行UI更新。

當(dāng)然了除了Handler這種通信方式外,線程間的通信還有其他幾種方式:管道Pip、共享內(nèi)存、通過文件及數(shù)據(jù)庫等。

我們主要來看下Handler以及其實現(xiàn)原理

一、Looper死循環(huán)詳解

1、死循環(huán)為什么不會導(dǎo)致應(yīng)用卡死ANR

線程默認(rèn)沒有Looper的,如果需要使用Handler就必須為線程創(chuàng)建Looper。

我們經(jīng)常提到的主線程,也叫UI線程,它就是ActivityThread,ActivityThread被創(chuàng)建時就會初始化Looper,這也是在主線程中默認(rèn)可以使用Handler的原因。

  1. public static void main(String[] args) { 
  2.     Looper.prepareMainLooper();//創(chuàng)建Looper和MessageQueue對象,用于處理主線程的消息 
  3.     ActivityThread thread = new ActivityThread(); 
  4.     thread.attach(false);//建立Binder通道 (創(chuàng)建新線程) 
  5.     if (sMainThreadHandler == null) { 
  6.         sMainThreadHandler = thread.getHandler(); 
  7.     } 
  8.     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); 
  9.     Looper.loop(); 
  10.     //如果能執(zhí)行下面方法,說明應(yīng)用崩潰或者是退出了... 
  11.     throw new RuntimeException("Main thread loop unexpectedly exited"); 

這個死循環(huán)會不會導(dǎo)致應(yīng)用卡死,即使不會的話,它會慢慢的消耗越來越多的資源嗎?

①對于線程即是一段可執(zhí)行的代碼,當(dāng)可執(zhí)行代碼執(zhí)行完成后,線程生命周期便該終止了,線程退出。而對于主線程,我們是絕不希望會被運行一段時間,自己就退出,那么如何保證能一直存活呢?簡單做法就是可執(zhí)行代碼是能一直執(zhí)行下去的,死循環(huán)便能保證不會被退出,例如,binder線程也是采用死循環(huán)的方法,通過循環(huán)方式不同與Binder驅(qū)動進(jìn)行讀寫操作,當(dāng)然并非簡單地死循環(huán),無消息時會休眠。但這里可能又引發(fā)了另一個問題,既然是死循環(huán)又如何去處理其他事務(wù)呢?通過創(chuàng)建新線程的方式。真正會卡死主線程的操作是在回調(diào)方法onCreate/onStart/onResume等操作時間過長,會導(dǎo)致掉幀,甚至發(fā)生ANR,looper.loop本身不會導(dǎo)致應(yīng)用卡死。

②主線程的死循環(huán)一直運行是不是特別消耗CPU資源呢?其實不然,這里就涉及到Linux pipe/epoll機(jī)制,簡單說就是在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此時主線程會釋放CPU資源進(jìn)入休眠狀態(tài),直到下個消息到達(dá)或者有事務(wù)發(fā)生,通過往pipe管道寫端寫入數(shù)據(jù)來喚醒主線程工作。這里采用的epoll機(jī)制,是一種IO多路復(fù)用機(jī)制,可以同時監(jiān)控多個描述符,當(dāng)某個描述符就緒(讀或?qū)懢途w),則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮鳎举|(zhì)同步I/O,即讀寫是阻塞的。所以說,主線程大多數(shù)時候都是處于休眠狀態(tài),并不會消耗大量CPU資源

2、主線程的消息循環(huán)機(jī)制是什么

主線程進(jìn)入死循環(huán)之前便創(chuàng)建了新binder線程,在代碼ActivityThread.main()中:

  1. public static void main(String[] args) { 
  2. //創(chuàng)建Looper和MessageQueue對象,用于處理主線程的消息 
  3.  Looper.prepareMainLooper(); 
  4.  //創(chuàng)建ActivityThread對象 
  5.  ActivityThread thread = new ActivityThread();  
  6.  //建立Binder通道 (創(chuàng)建新線程) 
  7.  thread.attach(false); 
  8.  Looper.loop(); //消息循環(huán)運行 
  9.  throw new RuntimeException("Main thread loop unexpectedly exited"); 
  • Activity的生命周期都是依靠主線程的Looper.loop,當(dāng)收到不同Message時則采用相應(yīng)措施:一旦退出消息循環(huán),那么你的程序也就可以退出了。從消息隊列中取消息可能會阻塞,取到消息會做出相應(yīng)的處理。如果某個消息處理時間過長,就可能會影響UI線程的刷新速率,造成卡頓的現(xiàn)象。
  • thread.attach(false)方法函數(shù)中便會創(chuàng)建一個Binder線程(具體是指ApplicationThread,Binder的服務(wù)端,用于接收系統(tǒng)服務(wù)AMS發(fā)送來的事件),該Binder線程通過Handler將Message發(fā)送給主線程。「Activity 啟動過程」比如收到msg=H.LAUNCH_ACTIVITY,則調(diào)用ActivityThread.handleLaunchActivity()方法,最終會通過反射機(jī)制,創(chuàng)建Activity實例,然后再執(zhí)行Activity.onCreate()等方法;
  • 再比如收到msg=H.PAUSE_ACTIVITY,則調(diào)用ActivityThread.handlePauseActivity()方法,最終會執(zhí)行Activity.onPause()等方法。
  • 主線程的消息又是哪來的呢?當(dāng)然是App進(jìn)程中的其他線程通過Handler發(fā)送給主線程

二、Handler機(jī)制原理詳解

Handler機(jī)制,主要牽涉到的類有如下四個,它們分工明確,但又相互作用

Message:消息

Hanlder:消息的發(fā)起者

Looper:消息的遍歷者

MessageQueue:消息隊列

1、 Looper.prepare()

  1. public static void prepare() { 
  2.         prepare(true); 
  3.     } 
  4.     private static void prepare(boolean quitAllowed) { 
  5.         // 規(guī)定了一個線程只有一個Looper,也就是一個線程只能調(diào)用一次Looper.prepare() 
  6.         if (sThreadLocal.get() != null) { 
  7.             throw new RuntimeException("Only one Looper may be created per thread"); 
  8.         } 
  9.         // 如果當(dāng)前線程沒有Looper,那么就創(chuàng)建一個,存到sThreadLocal中 
  10.         sThreadLocal.set(new Looper(quitAllowed)); 
  11.     } 

從上面的代碼可以看出,一個線程最多只有一個Looper對象。當(dāng)沒有Looper對象時,去創(chuàng)建一個Looper,并存放到sThreadLocal中,sThreadLocal是一個static的ThreadLocal對象,關(guān)于它的詳細(xì)使用,以后有機(jī)會再介紹,這里只要知道,它存儲了Looper對象的副本,并且可以通過它取得當(dāng)前線程在之前存儲的Looper的副本。如下圖:

接下來看Looper的構(gòu)造方法:

  1. private Looper(boolean quitAllowed) { 
  2.         // 創(chuàng)建了MessageQueue,并供Looper持有 
  3.         mQueue = new MessageQueue(quitAllowed); 
  4.         // 讓Looper持有當(dāng)前線程對象 
  5.         mThread = Thread.currentThread(); 
  6.     } 

這里主要就是創(chuàng)建了消息隊列MessageQueue,并讓它供Looper持有,因為一個線程最大只有一個Looper對象,所以一個線程最多也只有一個消息隊列。然后再把當(dāng)前線程賦值給mThread。

MessageQueue的構(gòu)造方法沒有什么可講的,它就是一個消息隊列,用于存放Message。

所以Looper.prepare()的作用主要有以下三點:

  • 創(chuàng)建Looper對象
  • 創(chuàng)建MessageQueue對象,并讓Looper對象持有
  • 讓Looper對象持有當(dāng)前線程

2、new Handler()

Handler有很多構(gòu)造方法,主要是提供自定義Callback、Looper等,我們先從最簡單的無參構(gòu)造方法看起:

  1. public Handler() { 
  2.         this(nullfalse); 
  3.     } 
  4.     public Handler(Callback callback, boolean async) { 
  5.       // 不相關(guān)代碼 
  6.        ...... 
  7.         //得到當(dāng)前線程的Looper,其實就是調(diào)用的sThreadLocal.get 
  8.         mLooper = Looper.myLooper(); 
  9.         // 如果當(dāng)前線程沒有Looper就報運行時異常 
  10.         if (mLooper == null) { 
  11.             throw new RuntimeException( 
  12.                 "Can't create handler inside thread that has not called Looper.prepare()"); 
  13.         } 
  14.         // 把得到的Looper的MessagQueue讓Handler持有 
  15.         mQueue = mLooper.mQueue; 
  16.         // 初始化Handler的Callback,其實就是最開始圖中的回調(diào)方法的2 
  17.         mCallback = callback; 
  18.         mAsynchronous = async; 
  19.     } 

首先,調(diào)用了Looper.myLooper,其實就是調(diào)用sThreadLocal.get方法,會得到當(dāng)前線程調(diào)用sThreadLocal.set保存的Looper對象,讓Handler持有它。接下來就會判斷得到的Looper對象是否為空,如果為空,就會報

"Can't create handler inside thread that has not called Looper.prepare(),這不就是我們之前在沒有調(diào)用Looper.prepare就在子線程中創(chuàng)建Handler時報的錯誤嘛。的確,當(dāng)我們沒有調(diào)用Looper.prepare(),則當(dāng)前線程中是沒有Looper對象的。

然后,讓Handler持有得到的Looper對象的MessageQueue和設(shè)置處理回調(diào)的Callback對象(最開始圖中的回調(diào)方法2)。

到這里,默認(rèn)的Handler的創(chuàng)建過程就結(jié)束了,主要有以下幾點:

  • 創(chuàng)建Handler對象
  • 得到當(dāng)前線程的Looper對象,并判斷是否為空
  • 讓創(chuàng)建的Handler對象持有Looper、MessageQueu、Callback的引用

3、Looper.loop()

  1. public static void loop() { 
  2.         // 得到當(dāng)前線程的Looper對象 
  3.         final Looper me = myLooper(); 
  4.         if (me == null) { 
  5.             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 
  6.         } 
  7.         // 得到當(dāng)前線程的MessageQueue對象 
  8.         final MessageQueue queue = me.mQueue; 
  9.         // 無關(guān)代碼 
  10.         ...... 
  11.         // 死循環(huán) 
  12.         for (;;) { 
  13.             // 不斷從當(dāng)前線程的MessageQueue中取出Message,當(dāng)MessageQueue沒有元素時,方法阻塞 
  14.             Message msg = queue.next(); // might block 
  15.             if (msg == null) { 
  16.                 // No message indicates that the message queue is quitting. 
  17.                 return
  18.             } 
  19.             // Message.target是Handler,其實就是發(fā)送消息的Handler,這里就是調(diào)用它的dispatchMessage方法 
  20.             msg.target.dispatchMessage(msg); 
  21.             // 回收Message 
  22.             msg.recycleUnchecked(); 
  23.         } 
  24.     } 

首先還是判斷了當(dāng)前線程是否有Looper,然后得到當(dāng)前線程的MessageQueue。接下來,就是最關(guān)鍵的代碼了,寫了一個死循環(huán),不斷調(diào)用MessageQueue的next方法取出MessageQueue中的Message,注意,當(dāng)MessageQueue中沒有消息時,next方法會阻塞,導(dǎo)致當(dāng)前線程掛起,后面會講到。

拿到Message以后,會調(diào)用它的target的dispatchMessage方法,這個target其實就是發(fā)送消息時用到的Handler。所以就是調(diào)用Handler的dispatchMessage方法,代碼如下:

  1. public void dispatchMessage(Message msg) { 
  2.         // 如果msg.callback不是null,則調(diào)用handleCallback 
  3.         if (msg.callback != null) { 
  4.             handleCallback(msg); 
  5.         } else { 
  6.             // 如果 mCallback不為空,則調(diào)用mCallback.handleMessage方法 
  7.             if (mCallback != null) { 
  8.                 if (mCallback.handleMessage(msg)) { 
  9.                     return
  10.                 } 
  11.             } 
  12.             // 調(diào)用Handler自身的handleMessage,這就是我們常常重寫的那個方法 
  13.             handleMessage(msg); 
  14.         } 
  15.     } 

可以看出,這個方法就是從MessageQueue中取出Message以后,進(jìn)行分發(fā)處理。

首先,判斷msg.callback是不是空,其實msg.callback是一個Runnable對象,是Handler.post方式傳遞進(jìn)來的參數(shù),后面會講到。而hanldeCallback就是調(diào)用的Runnable的run方法。

然后,判斷mCallback是否為空,這是一個Handler.Callback的接口類型,之前說了Handler有多個構(gòu)造方法,可以提供設(shè)置Callback,如果這里不為空,則調(diào)用它的hanldeMessage方法,注意,這個方法有返回值,如果返回了true,表示已經(jīng)處理 ,不再調(diào)用Handler的handleMessage方法;如果mCallback為空,或者不為空但是它的handleMessage返回了false,則會繼續(xù)調(diào)用Handler的handleMessage方法,該方法就是我們經(jīng)常重寫的那個方法。

關(guān)于從MessageQueue中取出消息以后的分發(fā),如下面的流程圖所示:

所以Looper.loop的作用就是:

從當(dāng)前線程的MessageQueue從不斷取出Message,并調(diào)用其相關(guān)的回調(diào)方法。

4、發(fā)送消息

使用Handler發(fā)送消息主要有兩種,一種是sendXXXMessage方式,還有一個postXXX方式,不過兩種方式最后都會調(diào)用到sendMessageDelayed方法,所以我們就以最簡單的sendMessage方法來分析。

我們先來看Handler的sendMessage方法:

  1. public final boolean sendMessage(Message msg) 
  2.         return sendMessageDelayed(msg, 0); 
  3.     } 
  4.     public final boolean sendMessageDelayed(Message msg, long delayMillis) 
  5.         if (delayMillis < 0) { 
  6.             delayMillis = 0; 
  7.         } 
  8.         return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); 
  9.     } 
  10.     public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 
  11.         // 這里拿到的MessageQueue其實就是創(chuàng)建時的MessageQueue,默認(rèn)情況是當(dāng)前線程的Looper對象的MessageQueue 
  12.         // 也可以指定 
  13.         MessageQueue queue = mQueue; 
  14.         if (queue == null) { 
  15.             RuntimeException e = new RuntimeException( 
  16.                     this + " sendMessageAtTime() called with no mQueue"); 
  17.             Log.w("Looper", e.getMessage(), e); 
  18.             return false
  19.         } 
  20.         // 調(diào)用enqueueMessage,把消息加入到MessageQueue中 
  21.         return enqueueMessage(queue, msg, uptimeMillis); 
  22.     } 
  23. 主要實現(xiàn)是調(diào)用enqueueMessage來實現(xiàn)的,看看該方法: 
  24.     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { 
  25.         // 把當(dāng)前Handler對象,也就是發(fā)起消息的handler作為Message的target屬性 
  26.         msg.target = this; 
  27.         if (mAsynchronous) { 
  28.             msg.setAsynchronous(true); 
  29.         } 
  30.         // 調(diào)用MessageQueue中的enqueueMessage方法 
  31.         return queue.enqueueMessage(msg, uptimeMillis); 
  32.     } 

首先,把當(dāng)前Handler作為Message的target屬性,方便Looper從MessageQueue中取出Message時進(jìn)行消息處理。然后調(diào)用了MessageQueue的enqueueMessage方法,把handler發(fā)送的消息加入到MessageQueue,供Looper去取出來處理。我們記下來看看。

MessageQueue的enqueueMessage方法:

  1. MessageQueue的enqueueMessage方法: 
  2.    boolean enqueueMessage(Message msg, long when) { 
  3.         if (msg.target == null) { 
  4.             throw new IllegalArgumentException("Message must have a target."); 
  5.         } 
  6.         // 一個Message,只能發(fā)送一次 
  7.         if (msg.isInUse()) { 
  8.             throw new IllegalStateException(msg + " This message is already in use."); 
  9.         } 
  10.         synchronized (this) { 
  11.             if (mQuitting) { 
  12.                 IllegalStateException e = new IllegalStateException( 
  13.                         msg.target + " sending message to a Handler on a dead thread"); 
  14.                 Log.w("MessageQueue", e.getMessage(), e); 
  15.                 msg.recycle(); 
  16.                 return false
  17.             } 
  18.             // 標(biāo)記Message已經(jīng)使用了 
  19.             msg.markInUse(); 
  20.             msg.when = when
  21.             // 得到當(dāng)前消息隊列的頭部 
  22.             Message p = mMessages; 
  23.             boolean needWake; 
  24.             // 我們這里when為0,表示立即處理的消息 
  25.             if (p == null || when == 0 || when < p.when) { 
  26.                 // 把消息插入到消息隊列的頭部 
  27.                 msg.next = p; 
  28.                 mMessages = msg; 
  29.                 needWake = mBlocked; 
  30.             } else { 
  31.                 // 根據(jù)需要把消息插入到消息隊列的合適位置,通常是調(diào)用xxxDelay方法,延時發(fā)送消息 
  32.                 needWake = mBlocked && p.target == null && msg.isAsynchronous(); 
  33.                 Message prev; 
  34.                 for (;;) { 
  35.                     prev = p; 
  36.                     p = p.next
  37.                     if (p == null || when < p.when) { 
  38.                         break; 
  39.                     } 
  40.                     if (needWake && p.isAsynchronous()) { 
  41.                         needWake = false
  42.                     } 
  43.                 } 
  44.                 // 把消息插入到合適位置 
  45.                 msg.next = p; // invariant: p == prev.next 
  46.                 prev.next = msg; 
  47.             } 
  48.             // 如果隊列阻塞了,則喚醒 
  49.             if (needWake) { 
  50.                 nativeWake(mPtr); 
  51.             } 
  52.         } 
  53.         return true
  54.     } 

首先,判斷了Message是否已經(jīng)使用過了,如果使用過,則直接拋出異常,這是可以理解的,如果MessageQueue中已經(jīng)存在一個Message,但是還沒有得到處理,這時候如果再發(fā)送一次該Message,可能會導(dǎo)致處理前一個Message時,出現(xiàn)問題。

然后,會判斷when,它是表示延遲的時間,我們這里沒有延時,所以為0,滿足if條件。把消息插入到消息隊列的頭部。如果when不為0,則需要把消息加入到消息隊列的合適位置。

最后會去判斷當(dāng)前線程是否已經(jīng)阻塞了,如果阻塞了,則需要調(diào)用本地方法去喚醒它。

以上是sendMessage的全部過程,其實就是把Message加入到MessageQueue的合適位置。那我們來簡單看看post系列方法:

  1. public final boolean post(Runnable r) 
  2.       return  sendMessageDelayed(getPostMessage(r), 0); 
  3.    } 
  4.    private static Message getPostMessage(Runnable r) { 
  5.        // 構(gòu)造一個Message,并讓其callback執(zhí)行傳來的Runnable 
  6.        Message m = Message.obtain(); 
  7.        m.callback = r; 
  8.        return m; 
  9.    } 

可以看到,post方法只是先調(diào)用了getPostMessage方法,用Runnable去封裝一個Message,然后就調(diào)用了sendMessageDelayed,把封裝的Message加入到MessageQueue中。

所以使用handler發(fā)送消息的本質(zhì)都是:把Message加入到Handler中的MessageQueue中去。

三、Handler 是如何能夠線程切換

Handler創(chuàng)建的時候會采用當(dāng)前線程的Looper來構(gòu)造消息循環(huán)系統(tǒng),Looper在哪個線程創(chuàng)建,就跟哪個線程綁定,并且Handler是在他關(guān)聯(lián)的Looper對應(yīng)的線程中處理消息的。

那么Handler內(nèi)部如何獲取到當(dāng)前線程的Looper呢—–ThreadLocal。

ThreadLocal可以在不同的線程中互不干擾的存儲并提供數(shù)據(jù),通過ThreadLocal可以輕松獲取每個線程的Looper。當(dāng)然需要注意的是:

①線程是默認(rèn)沒有Looper的,如果需要使用Handler,就必須為線程創(chuàng)建Looper。我們經(jīng)常提到的主線程,也叫UI線程,它就是ActivityThread;

②ActivityThread被創(chuàng)建時就會初始化Looper,這也是在主線程中默認(rèn)可以使用Handler的原因。

系統(tǒng)為什么不允許在子線程中訪問UI?這是因為Android的UI控件不是線程安全的,如果在多線程中并發(fā)訪問可能會導(dǎo)致UI控件處于不可預(yù)期的狀態(tài),那么為什么系統(tǒng)不對UI控件的訪問加上鎖機(jī)制呢?缺點有兩個:①首先加上鎖機(jī)制會讓UI訪問的邏輯變得復(fù)雜 ②鎖機(jī)制會降低UI訪問的效率,因為鎖機(jī)制會阻塞某些線程的執(zhí)行。所以最簡單且高效的方法就是采用單線程模型來處理UI操作。

四、Handler造成內(nèi)存泄露

1、引起內(nèi)存泄露原因

Java使用有向圖機(jī)制,通過GC自動檢查內(nèi)存中的對象(什么時候檢查由虛擬機(jī)決定),如果GC發(fā)現(xiàn)一個或一組對象為不可到達(dá)狀態(tài),則將該對象從內(nèi)存中回收。也就是說,一個對象不被任何引用所指向,則該對象會在被GC發(fā)現(xiàn)的時候被回收;另外,如果一組對象中只包含互相的引用,而沒有來自它們外部的引用(例如有兩個對象A和B互相持有引用,但沒有任何外部對象持有指向A或B的引用),這仍然屬于不可到達(dá),同樣會被GC回收。

Android中使用Handler造成內(nèi)存泄露的原因

  1. Handler mHandler = new Handler() { 
  2.     @Override 
  3.     public void handleMessage(Message msg) { 
  4.         mImageView.setImageBitmap(mBitmap); 
  5.     } 

上面是一段簡單的Handler的使用。當(dāng)使用內(nèi)部類(包括匿名類)來創(chuàng)建Handler的時候,Handler對象會隱式地持有一個外部類對象(通常是一個Activity)的引用(不然你怎么可能通過Handler來操作Activity中的View?)。而Handler通常會伴隨著一個耗時的后臺線程(例如從網(wǎng)絡(luò)拉取圖片)一起出現(xiàn),這個后臺線程在任務(wù)執(zhí)行完畢(例如圖片下載完畢)之后,通過消息機(jī)制通知Handler,然后Handler把圖片更新到界面。然而,如果用戶在網(wǎng)絡(luò)請求過程中關(guān)閉了Activity,正常情況下,Activity不再被使用,它就有可能在GC檢查時被回收掉,但由于這時線程尚未執(zhí)行完,而該線程持有Handler的引用(不然它怎么發(fā)消息給Handler?),這個Handler又持有Activity的引用,就導(dǎo)致該Activity無法被回收(即內(nèi)存泄露),直到網(wǎng)絡(luò)請求結(jié)束(例如圖片下載完畢)。另外,如果你執(zhí)行了Handler的postDelayed()方法,該方法會將你的Handler裝入一個Message,并把這條Message推到MessageQueue中,那么在你設(shè)定的delay到達(dá)之前,會有一條MessageQueue -> Message -> Handler -> Activity的鏈,導(dǎo)致你的Activity被持有引用而無法被回收。

2、解決方法

①在關(guān)閉Activity的時候停掉你的后臺線程。線程停掉了,就相當(dāng)于切斷了Handler和外部連接的線,Activity自然會在合適的時候被回收;

②如果你的Handler是被delay的Message持有了引用,那么使用相應(yīng)的Handler的removeCallbacks()方法,把消息對象從消息隊列移除就行了;

③將Handler聲明為靜態(tài)類,由于Handler不再持有外部類對象的引用,導(dǎo)致程序不允許你在Handler中操作Activity中的對象了。所以你需要在Handler中增加一個對Activity的弱引用(WeakReference)

  1. private static class MyHandler extends Handler {   
  2.         WeakReference<MainActivity> mActivity;   
  3.         MyHandler(MainActivity mActivity){   
  4.             this.mActivity = new WeakReference<MainActivity>(mActivity);   
  5.         }   
  6.         @Override   
  7.         public void handleMessage(Message msg) {   
  8.             switch(msg.what){   
  9.             case IMAGE_FAILURE:   
  10.                 Toast.makeText(mActivity.get()   
  11.                         , "Image Failure", Toast.LENGTH_LONG).show();   
  12.                 break;           
  13.             }   
  14.         }   
  15.     }  

總結(jié)

Handler消息機(jī)制主要的四個類的功能:

  • Message:信息的攜帶者,持有了Handler,存在MessageQueue中,一個線程可以有多個。
  • Hanlder:消息的發(fā)起者,發(fā)送Message以及消息處理的回調(diào)實現(xiàn),一個線程可以有多個Handler對象。
  • Looper:消息的遍歷者,從MessageQueue中循環(huán)取出Message進(jìn)行處理,一個線程最多只有一個。
  • MessageQueue:消息隊列,存放了Handler發(fā)送的消息,供Looper循環(huán)取消息,一個線程最多只有一個。

 

責(zé)任編輯:武曉燕 來源: Android開發(fā)編程
相關(guān)推薦

2021-09-09 06:55:43

AndroidViewDragHel原理

2021-08-17 13:41:11

AndroidView事件

2021-09-01 06:48:16

AndroidGlide緩存

2012-04-16 09:50:08

2021-10-15 09:19:17

AndroidSharedPrefe分析源碼

2015-09-24 17:41:15

Windows 10

2009-06-25 13:37:54

2021-09-06 13:12:05

前端JavaScript編程

2021-09-05 07:35:58

lifecycleAndroid組件原理

2014-05-22 15:41:59

Android消息處理機(jī)制Looper

2014-05-22 15:38:27

Android消息處理機(jī)制Looper

2014-05-22 15:00:16

Android消息處理機(jī)制Looper

2014-05-22 15:04:00

Android消息處理機(jī)制Looper

2014-05-22 15:07:44

Android消息處理機(jī)制Looper

2014-05-22 15:48:50

Android消息處理機(jī)制Looper

2014-05-22 14:57:28

Android消息處理機(jī)制Looper

2014-05-22 15:15:53

Android消息處理機(jī)制Looper

2014-05-22 15:18:25

Android消息處理機(jī)制Looper

2014-05-22 15:33:31

Android消息處理機(jī)制Looper

2014-05-22 15:45:58

Android消息處理機(jī)制Looper
點贊
收藏

51CTO技術(shù)棧公眾號