關於handler_loop機制:
1,什麼是handlerloop機制:
handler_loop機制,準確的說是handler,message,messageQueuen,loop機制,因爲他們當中,有兩個是底層中的,所以,我們所操作的只有handler,和message這兩個,爲了實現子線程中操作UI界面,Android中引入了Handler消息傳遞機制,目的是打破對主線程的依賴性。
2,handler_loop機制的作用
因爲大家都知道在子線程中無法對ui控件進行操作,會出現CalledFromWrongThreadException異常,
如果大量的數據操作在主線程中操作的話會因爲長時間的等待排序,超過5秒,導致anr的出現會報錯,所以,如何將子線程中的數據傳遞給主線程呢?在此,就可以通過handler機制進行操作。
3既然瞭解了這個機制,那麼什麼是handler呢?
handler,其實就是一個消息處理對象,他的作用就是將消息從一個線程,轉移到另一個線程,(這裏以主線程和子線程爲例);
只要你在子線程中,得到了主線程中的handler對象就可以通過handler.sendMessage的方法將數據轉移到主線程,然後就可以進行操作了。
4,什麼是Message.:
消息,被傳遞和處理的數據。其中包含了消息ID,消息處理對象以及處理的數據等,由MessageQueue統一列隊,終由Handler處理。通俗的講,就是handler要攜帶的數據。
5,什麼是MessageQueue:
MessageQueue:消息隊列,本質是一個數據結構,用來存放Handler發送過來的消息,並按照FIFO規則執行。當然,存放Message並非實際意義的保存,而是將Message串聯起來,等待Looper的抽取。其實就是讓傳遞過來的Messsage排隊等待的地方。
6,什麼是looper:
消息泵或循環器,不斷從MessageQueue中抽取Message。因此,一個MessageQueue需要一個Looper。如果你把MessageQueue當做一個傳送帶的話,那麼looper就是傳送帶終端接收處理產品的一個勞動者。接收穫取。
注意一點:
【重點】:使用Message需要注意4點:
1、Message雖然也可以通過new來獲取,但是通常使用Message.obtain()或Handler.obtainMessage()方法來從消息池中獲得空消息對象,以節省資源;
2、如果一個Message只需要攜帶簡單的int型數據,應優先使用arg1和arg2屬性來傳遞數據,這樣比其他方式節省內存;
3、儘可能使用Message.what來標識信息,以便用不同的方式處理Message;
4、如果需要從工作線程返回很多數據信息,可以藉助Bundle對象將這些數據集中到一起,然後存放到obj屬性中,再返回到主線程。
【備註:】
Looper對象用來爲一個線程開啓一個消息循環,從而操作MessageQueue;
默認情況下,Android創建的線程沒有開啓消息循環Looper,但是主線程例外。
系統自動爲主線程創建Looper對象,開啓消息循環;
所以主線程中使用new來創建Handler對象。而子線程中不能直接new來創建Handler對象就會異常。
子線程中創建Handler對象,步驟如下:
1,什麼是handlerloop機制:
handler_loop機制,準確的說是handler,message,messageQueuen,loop機制,因爲他們當中,有兩個是底層中的,所以,我們所操作的只有handler,和message這兩個,爲了實現子線程中操作UI界面,Android中引入了Handler消息傳遞機制,目的是打破對主線程的依賴性。
2,handler_loop機制的作用
因爲大家都知道在子線程中無法對ui控件進行操作,會出現CalledFromWrongThreadException異常,
如果大量的數據操作在主線程中操作的話會因爲長時間的等待排序,超過5秒,導致anr的出現會報錯,所以,如何將子線程中的數據傳遞給主線程呢?在此,就可以通過handler機制進行操作。
3既然瞭解了這個機制,那麼什麼是handler呢?
handler,其實就是一個消息處理對象,他的作用就是將消息從一個線程,轉移到另一個線程,(這裏以主線程和子線程爲例);
只要你在子線程中,得到了主線程中的handler對象就可以通過handler.sendMessage的方法將數據轉移到主線程,然後就可以進行操作了。
4,什麼是Message.:
消息,被傳遞和處理的數據。其中包含了消息ID,消息處理對象以及處理的數據等,由MessageQueue統一列隊,終由Handler處理。通俗的講,就是handler要攜帶的數據。
5,什麼是MessageQueue:
MessageQueue:消息隊列,本質是一個數據結構,用來存放Handler發送過來的消息,並按照FIFO規則執行。當然,存放Message並非實際意義的保存,而是將Message串聯起來,等待Looper的抽取。其實就是讓傳遞過來的Messsage排隊等待的地方。
6,什麼是looper:
消息泵或循環器,不斷從MessageQueue中抽取Message。因此,一個MessageQueue需要一個Looper。如果你把MessageQueue當做一個傳送帶的話,那麼looper就是傳送帶終端接收處理產品的一個勞動者。接收穫取。
注意一點:
【重點】:使用Message需要注意4點:
1、Message雖然也可以通過new來獲取,但是通常使用Message.obtain()或Handler.obtainMessage()方法來從消息池中獲得空消息對象,以節省資源;
2、如果一個Message只需要攜帶簡單的int型數據,應優先使用arg1和arg2屬性來傳遞數據,這樣比其他方式節省內存;
3、儘可能使用Message.what來標識信息,以便用不同的方式處理Message;
4、如果需要從工作線程返回很多數據信息,可以藉助Bundle對象將這些數據集中到一起,然後存放到obj屬性中,再返回到主線程。
【備註:】
Looper對象用來爲一個線程開啓一個消息循環,從而操作MessageQueue;
默認情況下,Android創建的線程沒有開啓消息循環Looper,但是主線程例外。
系統自動爲主線程創建Looper對象,開啓消息循環;
所以主線程中使用new來創建Handler對象。而子線程中不能直接new來創建Handler對象就會異常。
子線程中創建Handler對象,步驟如下:
Looper.prepare();
Handler handler = new Handler() {
//handlemessage(){}
}
Looper.loop();
說了這麼多現在再說一說消息傳遞的整個流程吧:(現附上一片代碼):
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PersistableBundle;
import com.yztc.mvpdemo.R;
/**
* Created by Administrator on 2016/11/17.
*/
public class Activitys extends Activity {
//聲明並創建handelr對象,用於子線程發送消息,並且用於主線程中接收消息
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {//此處的參數就是接收到的消息對象
super.handleMessage(msg);
//對消息對象進行判斷
switch (msg.what) {
//如果標籤是0的話,就操作0,標籤的handler發送的數據,因爲一個可已有多個handelr,所以接受的是以標籤是識別的
case 0:
//此處將傳遞來的數據進行操作,用於更新ui;
break;
}
}
};
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.activity_main);
//創建子線程用於進行大量的耗時操作
new Thread(new Runnable() {
@Override
public void run() {
//得到meddage對象,
Message message = Message.obtain();
//打上標籤
message.what = 0;
//將需要傳遞出去的數據傳遞出去,
message.obj = "呵呵,這裏是子線程";
//發送消息
handler.sendMessage(message);
}
}).start();
}
}
整個流程就結束了,因爲很多東西是在底層操作的,如果你不想深入瞭解,那麼你看到這個地方就可以結束了如果你想更加升入的瞭解一些底層的東西,可以繼續向下看:
首先我先將handelr,looper,message,messageQoueue中的一些方法展示出來:
Handler類中常用方法:
- handleMessage() 用在主線程中,構造Handler對象時,重寫handleMessage()方法。該方法根據工作線程返回的消息標識,來分別執行不同的操作。
- sendEmptyMessage() 用在工作線程中,發送空消息。
- sendMessage() 用在工作線程中,立即發送消息。
Message消息類中常用屬性:
- arg1 用來存放整型數據
- arg2 用來存放整型數據
- obj 用來存放Object數據
- what 用於指定用戶自定義的消息代碼,這樣便於主線程接收後,根據消息代碼不同而執行不同的相應操作。
A、Handler.java:(3個屬性,10個方法)
3個屬性:
- final MessageQueue mQueue; 封裝好的Message被handler發送出去,其實就是放到了MessageQueue消息隊列中。
- final Looper mLooper; 每個handler都有一個looper爲其不斷接收消息隊列中的消息,並返回給handler。
- final Callback mCallback; 被handler接收到的消息要通過Callback接口中的handleMessage()方法來進行處理。
10個方法:
- public boolean handleMessage(Message msg); Callback接口中的handleMessage()方法,用來處理返回給handler的消息的。
- public final Message obtainMessage() 獲取Message對象,其本質還是調用Message類的obtain()方法來獲取消息對象。
- public final boolean sendMessage(Message msg) 發送消息
- public final boolean sendEmptyMessage(int what) 發送只有what屬性的消息
- public final boolean post(Runnable r) 發送消息
- public final boolean postAtTime(Runnable r, long uptimeMillis) 定時發送消息
- public final boolean postDelayed(Runnable r, long delayMillis) 延遲發送消息
- public void dispatchMessage(Message msg) 分發消息。當Looper循環接收消息隊列中的消息時,就在不斷調用handler的分發消息方法,從而觸發Callback接口中的消息處理方法handleMessage()來對消息進行處理。
- public final boolean sendMessageDelayed(Message msg, long delayMillis) sendMessage()就是在調用延時發送消息的方法。
- public boolean sendMessageAtTime(Message msg, long uptimeMillis) 延時發送消息的方法就是在調用定時發送消息的方法。
- private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 將消息壓入消息隊列中
B、MessageQueue.JAVA:(1個方法)
- final boolean enqueueMessage(Message msg, long when) handler發送的消息正是通過該方法被加進了消息隊列中。因爲消息隊列中有消息,從而觸發了Looper通過loop()方法循環接收所有的消息。
C、Looper.JAVA:(3個屬性,5個方法)
3個屬性:
- static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 正是由於該線程本地變量,保證了一個線程中只能保存一個Looper對象,也就是說一個Thread中只能有一個Looper對象。
- final MessageQueue mQueue; 正是由於MessagQueue中的消息,才觸發了Looper的loop()方法。
- private static Looper mMainLooper = null; 該屬性是主線程中自動創建的Looper對象。
5個方法:
- public static void prepare() 每個handler所在的線程都必須有一個Looper提前準備好。
- public static void prepareMainLooper() 主線程中的的Looper已經被自動準備好。而該方法不需要手工調用。
- public static Looper getMainLooper() 獲取主線程中的Looper對象
- public static void loop() Looper的作用就是循環接收消息隊列中的消息。該方法中執行了一個無限循環。
- public static Looper myLooper() 該方法的作用是從線程本地變量中獲取到當前線程的Looper對象。
D、Message.java:(10個屬性,7個方法)
10個屬性:
- public int what; 該屬性一般用來標識消息執行的狀態。
- public int arg1; 用來存放整型數據的屬性。
- public int arg2; 用來存放整型數據的屬性。
- public Object obj; 用來存放複雜消息數據的屬性。
- Bundle data; 用來存放複雜消息數據的屬性。
- Handler target; 標識該消息要被髮送給哪個Handler對象。
- Message sPool; 消息池對象。
- int sPoolSize; 記錄消息池中剩餘消息的數量。
- int MAX_POOL_SIZE=50; 消息池中最大消息數量。
- Messenger replyTo; 定義消息的信使對象,將來可以用於跨APP的消息傳遞。
7個方法:
- public static Message obtain() 從消息池中獲取消息對象。
- public void recycle() 往消息池中歸還消息對象。
- public void setTarget(Handler target) 給消息handler對設置接收該消息的目標象。
- public Handler getTarget() 獲取消息的接收handler對象。
- public void sendToTarget() 將消息發送到目標handler對象。其本質是調用handler對象的sendMessage()方法來發送當前消息對象。
- public void setData(Bundle data) 將Bundle對象設置到message對象中Bundle屬性中。
- public Bundle getData() 從消息對象中獲取Bundle屬性的數據。
先說一下信息傳遞的過程:(消息傳遞的執行順序)
消息是如何從子線程中傳遞到主線程中的,中間發生了什麼?
開始傳遞handler.sendmeddage()---》調用的是底層的handler.sendMessageDelayed()方法---》繼續往底層深入handler.sendmessageAtTime()----->接着將消息傳遞給隊列讓隊列進行管理handler.enquouoMessage()-------->消息隊列接收到過來的消息messageQueue.enqueueMessage()--------->looper開始操作消息looper.loop()----->得到消息msg=messageQueue.next()------->分發消息msg.target.dispatchmessage(mdg)--------->msg的使命結束,開始進入回收被回收到了消息池中以供下次使用msg.recycle()----回收的同時----handlerMessage消息被傳入到主線程,兩者是幾乎同時進行的這樣整個消息傳遞結束,