Android Handler 、 Looper 、Message

Android Handler 、 Looper 、Message
聲明:此文非本人原創,為整理網絡資料加自己的一些注解所得。
參考:
http://blog.csdn.net/xukunhui2?viewmode=contents
http://blog.csdn.net/u012439416/article/details/52541671
https://blog.csdn.net/fightingxia/article/list/2

Binder主要用于跨進程通信,Handler主要用于進程內部進行通信,或者說進程內部不同線程之間進行通信,即是不同線程之間互相發送消息。

Handler:線程之間的切換
線程1:創建Handler對象mHandler,Looper對象、及MessageQueue(為主線程時只需創建Handler對象mHandler,其他本身已有)。接收message入隊messageQueue,當其message出隊時會通過Looper調用handle的handleMessage執行相應的操作。
線程2:用mHandler對象向線程1發送Message信息。

1、成員介紹
Message:主要功能是進行消息的封裝,同時可以指定消息的操作形式;
Looper:消息循環泵,用來為一個線程跑一個消息循環。每一個線程最多只可以擁有一個。
MessageQueue:就是一個消息隊列,存放消息的地方。以單鏈表的結構進行存儲,每一個線程最多只可以擁有一個。
Handler:消息的處理者,handler 負責將需要傳遞的信息封裝成Message,發送給Looper,繼而由Looper將Message放入MessageQueue中。當Looper對象看到MessageQueue中含有Message,就將其廣播出去。該handler 對象收到該消息后,調用相應的handler 對象的handleMessage()方法對其進行處理。
ThreadLocal:保證線程內數據安全。
2、同線程各成員的關系及數量
①一個線程中只能有一個Looper,只能有一個MessageQueue,可以有多個Handler,多個Messge;
②一個Looper只能維護唯一一個MessageQueue,可以接受多個Handler發來的消息;
③一個Message只能屬于唯一一個Handler;
④同一個Handler只能處理自己發送給Looper的那些Message;

一、Handler
1.定義:
主要接受子線程發送的數據, 并用此數據配合主線程更新UI。

解釋:當應用程序啟動時,Android首先會開啟一個主線程 (也就是UI線程) , 主線程為管理界面中的UI控件, 進行事件分發, 比如說, 你要是點擊一個 Button ,Android會分發事件到Button上,來響應你的操作。如果此時需要一個耗時的操作,例如: 聯網讀取數據, 或者讀取本地較大的一個文件的時候,你不能把這些操作放在主線程中,如果你放在主線程中的話,界面會出現假死現象, 如果5秒鐘還沒有完成的話,會收到Android系統的一個錯誤提示 "強制關閉"。 這個時候我們需要把這些耗時的操作,放在一個子線程中,因為子線程涉及到UI更新,Android子線程是線程不安全的。由于Handler運行在主線程中(UI線程中),它與子線程可以通過Message對象來傳遞數據, 這個時候,Handler就承擔著接受子線程傳過來的(子線程用sendMessage()方法傳遞)Message對象(里面包含數據) , 把這些消息放入主線程隊列中,配合主線程進行更新UI。

一個Handler會允許你發送和處理Message或者Runnable對象關聯到一個線程的消息隊列MessageQueue中,每一個Handler的實例都會關聯一個單一的線程和那個線程的消息隊列中。當你創建一個新的Handler,它會綁定到你創建的線程和這個線程消息隊列中。并且指向它,它會讓消息傳遞到關聯好它的消息隊列中,當它從消息隊列出隊的時候執行它。當你的應用程序的進程被創建的時候,它的主線程專門用來處理正常運行的主線程的消息隊列,(也就是說UI主線程有自己的消息隊列,所以我們沒必要在UI主線程中處理自己的消息)它關心的是管理頂層的應用對象(activities, broadcast receivers, etc)和他們創建的窗口。你可以創建你自己的線程,然后通過Handler與主線程溝通。就像上述說的通過post和sendMessage的方式,Runnable和Message會有計劃的執行在Handler的消息隊列中適時的進行處理。

2.Handler一些特點

handler可以分發Message對象和Runnable對象到主線程中, 每個Handler實例,都會綁定到創建他的線程中(一般是位于主線程),它有兩個作用:
(1)安排消息或Runnable 在某個主線程中某個地方執行;
(2)安排一個動作在不同的線程中執行。

Handler中分發消息的一些方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)

sendEmptyMessage(int)只發送一個帶有what屬性的消息。
sendMessage(Message)
sendMessageAtTime(Message,long)只發送一個帶有what屬性的消息,并且在一個指定的時間內(單位是ms)去發送。
sendMessageDelayed(Message,long)

以上post類方法允許你排列一個Runnable對象到主線程隊列中,
sendMessage類方法, 允許你安排一個帶數據的Message對象到主線程隊列中,等待更新。

  1. post和sendMessage本質上是沒有區別的,只是實際用法中有一點差別
  2. post也沒有獨特的作用,post本質上還是用sendMessage實現的,post只是一種更方便的用法而已

3.handler實例

子類需要繼承Hendler類,并重寫handleMessage(Message msg) 方法, 用于接受線程數據。
private Handler mHandler = new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 0://更新界面的數據
break;
}
}
}

mHandler.removeMessages(0);

二、Message
定義一個message包含描述信息和任意的數據對象發送給Handler。這個對象包含兩個額外的int類型的屬性和一個Object類型的屬性,它可以讓你不需要去做一些強制類型轉換的操作。

  1. arg1 和 arg2 都是Message自帶的用來傳遞一些輕量級存儲int類型的數據,比如進度條的數據等。這個數據是通過Bundle的方式來轉載的。
  2. obj 是Message自帶的Object類型對象,用來傳遞一些對象。兼容性最高避免對齊進行類型轉換等。
  3. replyTo 是作為線程通信的時候使用.
  4. what 用戶自定義的消息碼讓接受者識別消息種類,int類型。
    【注意】: 獲得Message的構造方法最好的方式是調用Message.obtain() 和 Handler.obtainMessage()方法。而不是直接用 new Message的方式來獲得Message對象。

調用obtain()方法來從消息池中獲得一個消息的對象的。然后在通過參數傳遞來封裝指定的Handler和需要攜帶的數據。如果使用這些重載的方法建議完成數據封裝之后調用sendToTarget()方法。
Message message = Message.obtain();
Bundle bundle = new Bundle();
data.putStringArray("str", new String[]{"AHui", "AHui1", "AHui2"});
message.setData(bundle);
message.obj = obj;
message.what = 0;
message.arg1 = 1;
message.arg2 = 3;
message.sendToTarget(); // 完成發送消息的動作
message 從handler 類獲取,從而可以直接向該handler 對象發送消息
handler.sendMessage(message); //直接調用 handler 的發送消息方法發送消息。

android 中發送消息不管是Message中的幾種重載的obtain()方式,還是Handler中的幾種重載的sendMessage最終都是通過Handler.sendMessage來發送的,而Handler中的幾種sendMessage()重載方法最終都會調用到sendMessageAtTime()方法來完成消息的入隊操作。
Looper類介紹
Looper與當前線程綁定,保證一個線程只會有一個Looper實例,同時一個Looper實例也只有一個MessageQueue.

Queue.loop()方法,不斷從MessageQueue中去取消息,交給消息的target屬性的dispatchMessage去處理。,在這個線程中調用prepare()方法來啟動一個循環,然后調用loop()就可以處理消息至到循環停止。
1.Looper.prepare(); 會初始化當前的線程關聯一個Looper.創建了一個MessageQueue(消息隊列)在一個線程中只能調用一次

2.Looper.loop()獲取Looper對象,然后將消息從Looper中取出,然后賦值給MessageQueue,讓MessageQueue去管理,接著在While(true)這個死循環里面一直在輪轉的取消息和分發消息(從Message msg = queue.next();和msg.target.dispatchMessage(msg);)這兩句代碼讀出。

3.mChildHandler.getLooper().quit();

三種啟用線程的方法
Android的CPU分配的最小單元是線程,Handler一般是在某個線程里創建的,因而Handler和Thread就是相互綁定的,一一對應。 而Runnable是一個接口,Thread是Runnable的子類。所以說,他倆都算一個進程。 HandlerThread顧名思義就是可以處理消息循環的線程,他是一個擁有Looper的線程,可以處理消息循環。與其說Handler和一個線程綁定,不如說Handler是和Looper一一對應的。Handler是溝通Activity 與Thread/runnable的橋梁。而Handler是運行在主UI線程中的,它與子線程可以通過Message對象來傳遞數據

1.通過繼承Thread類,并改寫run方法來實現一個線程
public class MyThread extends Thread {
//繼承Thread類,并改寫其run方法
public void run(){
}
}
啟動 new MyThread().start();

或者 new Thread() {
public void run() {
}
}.start();

2.創建一個Runnable對象
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();

3.通過Handler啟動線程

private Handler mHandler = new Handler();
private Runnable mRunnable = new Runnable() {
public void run() {
mHandler.postDelayed(mRunnable, 3000); //給自己發送消息,自運行
}
};
mHandler.post(mRunnable);

Thread主要函數有一個成員專門用于存儲線程的ThreadLocal的數據。
run()//包含線程運行時所執行的代碼
start()//用于啟動線程
sleep()/sleep(long millis)//線程休眠,交出CPU,讓CPU去執行其他的任務,然后線程進入阻塞狀態,sleep方法不會釋放鎖
yield()//使當前線程交出CPU,讓CPU去執行其他的任務,但不會是線程進入阻塞狀態,而是重置為就緒狀態,yield方法不會釋放鎖
join()/join(long millis)/join(long millis,int nanoseconds)//等待線程終止,直白的說 就是發起該子線程的線程 只有等待該子線程運行結束才能繼續往下運行
wait()//交出cpu,讓CPU去執行其他的任務,讓線程進入阻塞狀態,同時也會釋放鎖
interrupt()//中斷線程,自stop函數過時之后,我們通過interrupt方法和isInterrupted()方法來停止正在運行的線程,注意只能中斷已經處于阻塞的線程
getId()//獲取當前線程的ID
getName()/setName()//獲取和設置線程的名字
getPriority()/setPriority()//獲取和設置線程的優先級 一般property用1-10的整數表示,默認優先級是5,優先級最高是10,優先級高的線程被執行的機率高
setDaemon()/isDaemo()//設置和判斷是否是守護線程
currentThread()//靜態函數獲取當前線程
Thread線程主要狀態
(1) New 一旦被實例化之后就處于new狀態
(2) Runnable 調用了start函數之后就處于Runnable狀態
(3) Running 線程被cpu執行 調用run函數之后 就處于Running狀態
(4) Blocked 調用join()、sleep()、wait()使線程處于Blocked狀態
(5) Dead 線程的run()方法運行完畢或被中斷或被異常退出,線程將會到達Dead狀態

線程同步
(1)synchronized同步函數或同步代碼塊
(2)使用特殊域變量(volatile)實現線程同步
private volatile int count = 1000;
  a.volatile關鍵字為域變量的訪問提供了一種免鎖機制,
  b.使用volatile修飾域相當于告訴虛擬機該域可能會被其他線程更新,
  c.因此每次使用該域就要重新計算,而不是使用寄存器中的值
  d.volatile不會提供任何原子操作,它也不能用來修飾final類型的變量
對于Handler來說,它需要獲取當前線程的Looper,通過ThreadLocal就可以輕松實現Looper在線程中的存取.如果不采用ThreadLocal,那么系統就必須提供一個全局的哈希表供Handler查找指定線程的Looper,這樣一來就必須提供一個類似于LooperManager的類了,這就是ThreadLocal的好處。所以, 當某些數據是以線程為作用域并且不同線程具有不同的數據副本的時候,就可以考慮采用ThreadLocal。比如Looper、ActivityThread以及AMS等都用到了ThreadLocal。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 前言 在Android開發的多線程應用場景中,Handler機制十分常用 今天,我將手把手帶你深入分析Handle...
    BrotherChen閱讀 486評論 0 0
  • 1. ANR異常 Application No Response:應用程序無響應。在主線程中,是不允許執行耗時的操...
    JackChen1024閱讀 1,456評論 0 3
  • 1. 前言 在之前的圖解Handler原理最后留下了幾個課后題,如果還沒看過那篇文章的,建議先看那篇文章,課后題如...
    唐江旭閱讀 6,009評論 5 45
  • 異步消息處理線程啟動后會進入一個無限的循環體之中,每循環一次,從其內部的消息隊列中取出一個消息,然后回調相應的消息...
    cxm11閱讀 6,455評論 2 39
  • Android Handler機制系列文章整體內容如下: Android Handler機制1之ThreadAnd...
    隔壁老李頭閱讀 20,530評論 13 54