在Android中,有一個規定就是除了主線程,其他線程不能操作UI視圖,因爲不這樣做的話會出現線程不安全問題。但還有一個規定UI線程在執行一個操作如果5秒內沒有響應就會包ANR錯誤。所以UI線程中不允許訪問網絡這樣的耗時操作,那麼問題來了,子線程執行耗時操作,比如從網絡獲取圖片,但子線程不能更新UI,而主線程能更新UI,但不能去下載圖片。這樣handler消息處理機制就出現了。
1. Handler是什麼?
handler是Android給我們提供用來更新UI的一套機制,也是一套消息處理的機制,我們可以發送消息,也可以通過它處理消息。
2. 爲什麼要使用Handler?
Android在設計時,就封裝了一套消息消息創建、傳遞、處理機制,如果不遵循這樣的機制就沒辦法更新UI,就會拋出異常。
3. Android中爲什麼要設計只能通過Handler機制更新UI?
解決多線程併發問題。如果允許多線程更新UI會導致界面錯亂,如果非要使用多線程並使用加鎖機制更新UI又會導致性能下降,所以更新UI的操作全部交給主線程。
4.那麼handler機制是怎樣的機制呢?
瞭解handler機制,首先需要清楚四個核心類:
Message:對發送的消息的封裝
MessageQueue:消息隊列,存放所有的消息
Looper:可以循環讀取消息(從MessageQueue中讀取)
Handler:處理消息,同時也是發送消息的
具體來看看handler是怎麼實現的。
4.1使用1:主線程接收子線程發來的消息(下載圖片爲例)
1)在主線程中初始化handler對象:
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//處理子線程發送過來的message
Bitmap bitmap = (Bitmap)msg.obj;
imageView.setImageBitmap(bitmap);
}
};
2)然後子線程中下載好圖片後發送圖片給主線程:
new Thread(new Runnable() {
@Override
public void run() {
HttpGet get = new HttpGet(path);
HttpClient client = new DefaultHttpClient();
HttpResponse response = null;
try {
response = client.execute(get);
if (response.getStatusLine().getStatusCode() == 200) {
byte[] arr = EntityUtils.toByteArray(response
.getEntity());
Bitmap bitmap = BitmapFactory.decodeByteArray(arr, 0,
arr.length);
// 下載完成時把圖片發送給主線程
// 從MessageQueue中獲取可用的Message對象,如果沒有可用的Message對象則會創建一個新的Message對象
Message msg = Message.obtain();
// 把發送的圖片封裝到msg中
msg.obj = bitmap;
// 使用Handler發送msg
handler.sendMessage(msg);// 把msg發送給實例化handler的線程
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
消息的攔截:
/**
* 攔截消息測試
* @author Administrator
*
*/
public class ThirdActivity extends Activity {
Handler handler = new Handler(new Callback() {
@Override
public boolean handleMessage(Message msg) {
Log.i("--", "消息都要經過我這裏");
/**返回false,消息會繼續向下分發,返回true則攔截*/
return true;
}
}) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.i("--", "我纔是處理消息的");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
handler.sendEmptyMessage(1);
}
}).start();
}
}
3)使用1有關handler機制的操作有:
Handler handler = new Handler();
handleMessage(Message msg);
Message msg = Message.obtain();
handler.sendMessage(msg);
那麼現在來看一看它內部到底是怎樣執行的。
5.從源碼一步一步分析handler原理
5.1Handler handler = new Handler();
public Handler() {
this(null, false);
}
這個無參構造方法會調用兩個參數的構造方法,注意上面的參數null和false,如下:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
那來看看這個兩個參數的構造方法都做了些什麼。
FIND_POTENTIAL_LEAKS這個變量初始化爲false
private static final boolean FIND_POTENTIAL_LEAKS = false;
所以直接執行後面的,也就是這些:
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
這幾句的操作就是得到一個Looper對象和一個MessageQueue對象,mLooper = Looper.myLooper();中Looper.myLooper()方法中是得到當前線程的Looper對象。那麼當前線程是什麼?在這裏,因爲我們是在主線程中實例化Handler對象,所以當前線程就是主線程,值得注意的是,主線程在創建時就會維護一個Looper對象和MessageQueue對象,所以這裏得到的Looper對象和消息隊列都是主線程的。
public static Looper myLooper() {
return sThreadLocal.get();
}
mQueue = mLooper.mQueue;這句說明handler內部的MessageQueue和Looper的MessageQueue是一個。
好,那麼handler的初始化階段先到這裏。
5.2 Message msg = Message.obtain();
再來看看這句做了哪些事,
注意:有時候我們會直接這麼寫Message msg = new Message();
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
sPoolSize--;
return m;
}
}
return new Message();
}
這裏的意思就是如果sPool不爲空就返回sPool,否則就new一個新的Message對象。(sPool是回收放在消息池中的對象)
public void recycle() {
clearForRecycle();
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
5.3 handler.sendMessage(msg);
關鍵的一句終於到了,來看看這個又做了什麼。
第一步:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
第二步:
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
第三步:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
第四步:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
到第四步算是找到能辦事的了,看這裏都辦了哪些事。
1) msg.target = this;可以查看Message源碼target就是Handler類型的,這裏把msg的target指向this,也就是這個發送消息的handler。
2)queue.enqueueMessage(msg, uptimeMillis);調用queue的enqueueMessage方法,去MessageQueue類中看看。
final boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}
boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}
msg.when = when;
Message p = mMessages;
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;
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}
很簡單,就是把handler發送的msg加入到消息隊列中。
5.4handleMessage(Message msg);
好了,最後只用處理msg了。這是個回調方法,所以必須要等到Looper去從消息隊列中讀取消息時纔會執行。
那就去看看Looper.loop();這個循環讀取消息的方法。
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;
// 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();
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);
if (logging != null) {
logging.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(TAG, "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);
}
msg.recycle();
}
}
開頭幾句是得到當前線程的Looper對象,然後同構Looper對象得到queue對象,很明顯,5.1已經說了,那麼這裏得到的就是主線程那個Looper和MessageQueue。然後開始了死循環,一直從消息隊列中讀取消息,讀不到就返回,否則執行後面操作。注意看後面操作,真正處理消息的時候到了。
msg.target.dispatchMessage(msg);關鍵就是這句。 它調用了msg的target的dispatchMessage方法,之前說了,target就是發送消息的那個handler,好,再會Handler類看dispatchMessage方法。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
很簡單,這就是一個消息分發處理嘛。記得handler發送消息有兩種方式,一種就是普通的handler.sendMessage(msg);方法,還有一種就是
handler.post(new Runnable(){
@Override
public void run() {
//主線程應該執行的操作
}
});
先來看第一種:直接回調hanleMessage(msg);方法,這樣主程序中就執行該操作啦。
再來看第二種:
private static void handleCallback(Message message) {
message.callback.run();
}
調用message.callback的run方法。
從源碼可以一步一步看出,如下,message的callback指向handler調用post方法傳入的Runnable對象。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
再回頭看dispatchMessage方法,會調用下面這個方法:
private static void handleCallback(Message message) {
message.callback.run();
}
內部最後調用run方法。
handler原理基本就這樣了。
總結:handler對象定義在需要接收本線程或其他線程發送消息的線程中,該線程會維護一個Looper對象和MessageQueue對象,在其他線程或本線程通過handler發送的消息會加入到handler所在線程中的消息隊列中,同時Looper的loop()方法會一直讀取消息隊列,讀到消息後,又讓發送消息的handler處理這個消息。