最近看了《Android內核剖析》這本書,,將學習筆記整理如下
1. 異步消息線程
異步消息線程不同一般線程的是,,它的線程run方法有一個無限循環(huán),沒循環(huán)一次,,從其內部的消息隊列中取出一個消息并調用回調函數(shù)進行處理,。如果消息隊列為空,線程暫停,,直到消息隊列中有新的消息,。
一般而言有兩種需求需要用到異步線程處理:
(1) 任務需要常駐
(2) 任務需要根據外部傳遞的消息做出不同的操作
2. Android異步線程的實現(xiàn)方法
在線程的內部有一個或多個Handler對象,外部程序通過該handler對象向線程發(fā)送異步消息,,消息經由Handler傳遞到MessageQueue對象中,。線程內部只能包含一個MessageQueue對象,線程主執(zhí)行函數(shù)從MessageQueue中讀取消息,,并回調Handler對象中的回調函數(shù)handleMessage(),。下面的代碼是一個簡單的實例。
- class LooperThread extends Thread {
- public Handler mHandler;
-
- public void run() {
- Looper.prepare();
-
- mHandler = new Handler() {
- public void handleMessage(Message msg) {
- // process incoming messages here
- }
- };
-
- Looper.loop();
- }
- }
2.1 Looper
Looper的作用有兩點,,第一是調用靜態(tài)函數(shù)prepare()為線程創(chuàng)建一個消息隊列,;第二是調用靜態(tài)函數(shù)loop(),使調用該函數(shù)的線程進行無限循環(huán),,并從消息隊列中讀取消息,。
(1)調用prepare()函數(shù)
- public static final void prepare() {
- if (sThreadLocal.get() != null) {
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper());
- }
//Looper構造函數(shù)
- private Looper() {
- mQueue = new MessageQueue();
- mRun = true;
- mThread = Thread.currentThread();
- }
在代碼中,變量sThreadLocal的類型是ThreadLocal,,該類的作用是提供“線程局部存儲”,,從變量的作用域來理解下這個概念。
|
|
函數(shù)成員變量-------------------------------- |
僅在函數(shù)內部有效 |
類成員變量 --------------------------------- |
僅在對象內部有效 |
線程局部存儲(TLS)變量-------------------- |
在本線程內的任何對象內保持一致 |
類靜態(tài)變量------------------------------------ |
在本進程內的任何對象內保持一致 |
跨進程通信(IPC)變量----------------------- |
一般使用Binder進行定義,,在所有進程中保持一致 |
從上述的代碼可以看到,,一個線程只允許創(chuàng)建一個Looper對象,這是因為每個Looper對象都會創(chuàng)建一個MessageQueue對象,,一個異步線程中只能有一個消息隊列,,所以也就只能有一個Looper對象
(2)調用loop()函數(shù)
- public static final void loop() {
- Looper me = myLooper();
- MessageQueue queue = me.mQueue;
-
- // Make sure the identity of this thread is that of the local process,
- // and keep track of what that identity token actually is.
- Binder.clearCallingIdentity();
- final long ident = Binder.clearCallingIdentity();
-
- while (true) {
- // might block 如果隊列為空,則當前線程就會被掛起,,next()函數(shù)內部會暫停線程
- Message msg = queue.next();
-
- if (msg != null) {
- if (msg.target == null) {
- // No target is a magic identifier for the quit message.
- return;
- }
- if (me.mLogging!= null) me.mLogging.println(
- ">>>>> Dispatching to " + msg.target + " "
- + msg.callback + ": " + msg.what
- );
- //回調函數(shù)完成對消息的處理,。msg.target的類型是Handler,msg最終交予handleMessage()處理
- msg.target.dispatchMessage(msg);
- if (me.mLogging!= null) me.mLogging.println(
- "<<<<< Finished to " + msg.target + " "
- + msg.callback);
-
- // Make sure that during the course of dispatching the
- // identity of the thread wasn't corrupted.
- final long newIdent = Binder.clearCallingIdentity();
- if (ident != newIdent) {
- Log.wtf("Looper", "Thread identity changed from 0x"
- + Long.toHexString(ident) + " to 0x"
- + Long.toHexString(newIdent) + " while dispatching to "
- + msg.target.getClass().getName() + " "
- + msg.callback + " what=" + msg.what);
- }
- //回收message對象占用的系統(tǒng)資源
- msg.recycle();
- }
- }
- }
2.2 MessageQueue
消息隊列采用排隊方式對消息進行處理,,即先到的消息會先得到處理,,但如果消息本身指定了被處理的時刻,,則必須等到該時刻才能處理該消息。
2.3 Handler
盡管MessageQueue提供了直接讀/寫的函數(shù)接口,,但對于應用程序員而言,,一般不直接讀/寫消息隊列。
程序員一般使用Handler類向消息隊列發(fā)送消息,,并重載Handler的handleMessage函數(shù)添加消息處理代碼,。
handler對象只能添加到有消息隊列的線程中,否則會發(fā)生異常,。因此,在構造Handler對象前,,必須已經執(zhí)行過Looper.prepare(),,但prepare()不能被執(zhí)行兩次。
一個線程中可以包含多個Handler對象,。在Looper.loop函數(shù)中,,不同的Message對應不同的Handler對象,從而回調不同的handleMessage函數(shù),。
異步消息處理線程處理用于多線程的消息傳遞外,,它還和跨進程調用(IPC)一起被使用,用于實現(xiàn)異步跨進程調用,。
|