編輯:關於Android編程
說來有些慚愧,對於這三者的初步認識居然是在背面試題的時候。那個時候自己接觸Android的時間還不長,學習的書籍也就是比較適合入門的《瘋狂Android講義》,當然在學到Handler這一部分的時候,書中也是有提到一些簡單示例,後來在工作中需要用到這個MessageQueue的時候才開始真正琢磨了一下這三者的聯系。如果想要對這三者好好理解一番,個人還是比較推薦《深入理解Android卷Ⅰ》。以下對這三者之間的恩怨糾葛的介紹和分析也是參考這本書的相關章節,算是一篇讀書筆記吧。
Android的消息傳遞機制是另一種形式的“事件處理”,這種機制主要是為了解決Android應用中的多線程問題——Android平台只允許UI線程修改Activity中的UI組件,這就使得新啟動的線程無法去動態修改界面組件中的屬性值。但是我們的程序界面不可能是一個靜態的呈現,所以這就必須用到本博客中提到的三個大類了。
public class LooperThreadActivity extends Activity {
private final int MSG_HELLO = 0;
private Handler mHandler;
private CustomThread mThread = null;
private static final String TAG = LooperThreadActivity.class.getSimpleName();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mThread = new CustomThread();
mThread.start();
Button sendButton = (Button) findViewById(R.id.send_button);
final EditText contentEditText = (EditText) findViewById(R.id.content_edittext);
sendButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String msgText = contentEditText.getText().toString();
sendMessage(msgText);
}
});
}
private void sendMessage(String content) {
Toast.makeText(this, send msg: + content, 0).show();
// TODO 1.向MessageQueue中添加消息
// 通過Message.obtain()來從消息池中獲得空消息對象,以節省資源
Log.i(TAG, ------------> send msg 1.);
Message msg = mHandler.obtainMessage(MSG_HELLO, content);
msg.sendToTarget();
Log.i(TAG, ------------> send msg 2.);
Message msg2 = mHandler.obtainMessage(MSG_HELLO, content + 2);
msg2.sendToTarget();
}
class CustomThread extends Thread {
@Override
public void run() {
Looper.prepare();
Log.i(TAG, ------------> loop.pre.);
mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_HELLO:
Log.i(TAG, ------------> receive msg.);
Toast.makeText(LooperThreadActivity.this, receive msg: + (String) msg.obj, 0).show();
}
}
};
Looper.loop();
}
}
}


大家可以看到我做了連續兩次的添加消息數據,在結果中也有很好的體現,不過Looper.prepare();和Handler之間的內容卻只執行了一次。這是因為我們自定義的線程CustomThread只被start了一次,且start過後一直存在,沒有被銷毀,所以Looper一直存在,MessageQueue一直存在,從而保證了一個Thread只能有一個Looper對象。對於這一點下面會用源碼進行進一步的說明。
就應用程序而言,Android系統中Java的應用程序和其他系統上相同,都是靠消息驅動來工作的。Android系統中的消息驅動離不開Looper、Handler和Message這三者,雖說不上哪個更重要一些,不過相對突出的的確是Looper。下面就對這些類逐一地介紹。
跟蹤prepare()進入Android的源碼,我們可以發現以下源代碼:
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));
}
sThreadLocal定義:
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocalsThreadLocal = new ThreadLocal ();
從以上源碼中我們可以看到,在調用prepare的線程中,設置了一個Looper對象,這個Looper對象就保存在這個調用線程的TLV中。而Looper對象內部封裝了一個消息隊列。也就是說prepare通過ThreadLocal機制,把Looper和調用線程關聯在了一起。
跟蹤loop()進入Android的源碼(此處刪除了一些暫時不太關聯的代碼):
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;
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
msg.target.dispatchMessage(msg);
msg.recycle();
}
}
通過上面的分析,Looper有以下幾個作用:
封裝了一個消息隊列.prepare函數把當前的Looper和調用prepare的線程(即最終的處理線程)綁定在了一起.處理線程調用loop,處理來自該消息隊列中的消息. 當事件源向這個Looper發送消息的時候,其實就是把消息加到這個Looper的消息列隊裡了。那麼,該消息就將由和Looper綁定的處理來處理。
學習Handler之初先來認識一下Handler中所包含的部分成員:
final MessageQueue mQueue; final Looper mLooper; final Callback mCallback;在Handler類中,它的構造函數會把Handler中的消息隊列變量最終都會指向Looper的消息隊列。由於是被指向,那麼Handler中的消息隊列其實就是某個Looper的消息隊列。
Looper和Handler之間其實是存在著同步關系的。這裡對它們之間的同步關系不做過多介紹,如果想了解可以參看《深入理解Android卷Ⅰ》第128頁。筆者在此只提出一個提醒點:由於HandlerThread完美地解決了Looper和Handler同步過程中可能出現的空指針異常問題,所以在以後的開發過程中,我們還是多用HandlerThread吧。當然如果不想使用它,那就請使用鎖機制來健壯你的代碼吧,不過這就可能會落下重復造輪子的口舌了。
雖然已經“稀裡糊塗”到了結尾,不過對Looper、Handler和Message的認識的確進了一大步。希望看完本文的你也有所收獲。
Android中利用matrix 控制圖片的旋轉、縮放、移動
本文主要講解利用android中Matrix控制圖形的旋轉縮放移動,具體參見一下代碼:復制代碼 代碼如下:/** * 使用矩陣控制圖片移動、縮放、旋轉 &nb
java/android 設計模式學習筆記(9)---代理模式
這篇博客我們來介紹一下代理模式(Proxy Pattern),代理模式也成為委托模式,是一個非常重要的設計模式,不少設計模式也都會有代理模式的影子。代理在我們日常生活中也
Android學習之Flux架構入門
Flux 架構介紹Flux 架構 被Facebook使用來構建他們的客戶端web應用。跟Clean Architecture一樣,它不是為移動應用設計的,但是它的特性和簡
Android體系架構詳解
本文講述的Android系統體系架構,是指應用層之下的整個系統內部的架構層級關系。而並非常說的4層架構:應用層,framework,運行庫與環境,Linux