編輯:關於Android編程
Android Handler消息機制,在Android中扮演了很重要的角色。通過Android Handler,能夠實現延遲處理、子線程更新主線程界面等功能。(Android更新UI,一定需要在主線程中,這個規定的原因是,多個線程同時更新UI會造成UI界面更新混亂,不利於管理,因此,程序在初始化時,主線程中就初始化了UIHandler)。
Handler機制主要涉及到幾個類:
- Handler
- Looper
- ThreadLocal
- MessageQueue
後面將從簡單到復雜分析他們各自的功能,以及之間的關系。
ThreadLocal是一個線程相關的存儲類。ThreadLocal的存儲,獲取數據:
public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }
public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); }
ThreadLocal中常用的方法主要是這兩個:
- set(T value)方法,傳入一個泛型對象,首先獲取當前線程的對象,然後values(currentThread),這個方法其實獲取currentThread.loacalValues,也就是說每個Thread都有localValues對象。如果這個values為空,則初始化,否則直接put添加這個數據。我們查看Values這個類的put方法:
void put(ThreadLocal key, Object value) { cleanUp(); // Keep track of first tombstone. That's where we want to go back // and add an entry if necessary. int firstTombstone = -1; for (int index = key.hash & mask;; index = next(index)) { Object k = table[index]; if (k == key.reference) { // Replace existing entry. table[index + 1] = value; return; } if (k == null) { if (firstTombstone == -1) { // Fill in null slot. table[index] = key.reference; table[index + 1] = value; size++; return; } // Go back and replace first tombstone. table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++; return; } // Remember first tombstone. if (firstTombstone == -1 && k == TOMBSTONE) { firstTombstone = index; } } }
首先cleanup,這個方法會調用rehashed,如果數組容納的空間不夠,會進行空間擴展。然後外層是循環,循環的第一個判斷,如果找到原來設置的key,則直接修改原來key對應的值(保存的時候,是index保存鍵,index+1保存值);循環的第二個判斷,如果沒有找到對應的key,並且firstTombstone=-1,則在最後面保存key和value;第三個判斷,如果k!=null並且firstTombstone=-1和k==TOMBSTONE,那麼firstTomebstone=index,然後在下次循環中,將數據賦值到index這個位置(因為這個位置的數據是被刪除的位置,被刪除的數據會被賦值為TOMBSTONE)。
總結一下ThreadLocal:
1. 每個Thread都有一個Values對象,這個對象用來存儲數據。
1. ThreadLocal操作對應線程的數據,其實是操作在線程的Values中的數據。
1. put方法,會先檢查數組容量,然後將數據保存在數據中,如果其中有廢棄的數據,則會這個位置的數據會被覆蓋。
1. get方法,在線程的values中,查找指定位置的值。這個位置通過ThreadLocal的hash和Values的mask值來計算。
2. ThreadLocal是線程級保存數據的。
它保存了由Looper分發的一系列事件Message,MessageQueue主要用來操作Message消息的,它核心的方法主要是:enqueueMessage,next,quite。
enqueueMessageboolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }
分析一下上面方法的流程:
1. 首先判斷這個Message的Handler是否為空以及消息是否已經在使用。
1. 然後同步方法中,先判斷當前消息隊列是否退出,如果退出了則拋出異常,並將這個消息回收掉。
2. 如果消息隊列沒有退出,則開始將消息添加到隊列中:先將消息設置為正在使用中狀態(makeInUse),如果當前頭結點為空,則將這個消息Message作為頭結點,頭結點的名稱依然還是mMessages,這時,重新將阻塞的線程激活
3. 如果頭結點不為空,則先執行for循環,將p指向到隊列的末尾結點的next結點,最後將Message添加末尾結點的next結點,而Message的下一個結點指向p這個空結點。這樣就把新消息添加到了隊列末尾。
示意圖(將消息插入末尾):
void quit(boolean safe) { if (!mQuitAllowed) { throw new IllegalStateException("Main thread not allowed to quit."); } synchronized (this) { if (mQuitting) { return; } mQuitting = true; if (safe) { removeAllFutureMessagesLocked(); } else { removeAllMessagesLocked(); } // We can assume mPtr != 0 because mQuitting was previously false. nativeWake(mPtr); } }
主要是兩個判斷
1. 是否運行退出循環,主線程中的Looper是不允許的。
2. 是否安全退出,非安全退出,則隊列中所有的消息將會因為調用removeAllMessagesLocked()移除;安全退出只會刪除時間大於當前的消息,已經在隊列中可執行的消息不會刪除。
Looper的主要作用是在當前線程中運行一個消息循環隊列。sMainLooper是一個靜態變量,在主線程調用prepareMainLooper時候就會被賦值。因此這個的獲取就很簡單,調用getMainLooper就ok了。
後面主要講講Looper的核心方法,按照Looper的使用方法順序,prepare(),loop(),quite()方法說明。
prepare
public static void prepare() { prepare(true); } 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)); }
Looper的初始化直接調用prepare靜態方法,會先new一個Looper對象,然後保存在當前線程中。
loop
public static void loop() { ... for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); ... } }
上面是loop方法的核心代碼,主要是通過死循環不斷的調用queue.next()獲取隊列中的消息,並調用dispatchMessage將消息發送給Handler。消息隊列的next()方法當消息隊列中沒有消息後,會阻塞;當調用了quit方法後,next()返回null,然後loop的循環也會退出。總體來說,流程比較簡單。
Handler是發送消息和處理消息的主要參與者。下面主要分析發送消息和處理消息的相關方法:
enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
可以發現,Handler裡面的sendMessage或者是post相關方法,最終都會調用enqueueMessage方法,最後其實是MessageQueue的enqueueMessage方法,這就回到上面MessageQUeue分析的過程了。
dispatchMessage
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
首先判斷Message有沒有設置callback,如果設置了直接調用callback處理,否則調用調用Handler的Callback或者是調用handleMessage方法處理。
以上就是Android中Handler的源碼分析。接下來我們通過常用的使用方式結合源碼再梳理一遍。
Looper.preprare() Looper.looper Handler handler = new Hander(looper) hanlder.sendMessage handleMessage
上面是整個用法的簡單寫法,Looper保存在prepare方法調用的所在線程中,Handle可以在其他線程中創建,只要能拿到looper對象,Handler的方法也可以在其他線程中調用。如果不指定looper對象,Handler綁定的就是當前線程的Looper對象。Looper對象的loop方法決定了Handler的handleMessage方法處理操作所在的線程。
到此為止,Handler的機制基本就結束了。
上一篇我們通過實例學習了MINA框架的用法,發現用起來還是挺方便的,就那麼幾步就可以了,本著學東西必知其原理的觀念,決定看看MINA的源碼實現,好了,我們開始吧!MINA
關鍵詞:藍牙硬件接口 UART PCM blueZ 版本:基於android4.2之前版本 bluez內核:linux/linux3.08系統:android/and
前言 關鍵字:Vitamio、VPlayer、Android播放器、Android影音、Android開源播放器 本章節把Android萬能播放器本地播放的主要功能(
在Android開發過程中,總要給app添加一些背景,個人認為使用純色調便可以達到優雅的視覺效果。補充一些常用的顏色值:colors.xml #ffffff #f