一篇就夠了系列之Handler全解析
前言:
兩年前寫過一篇Java中的多執行緒Thread Runnable及android的handler,現在想從原始碼角度好好分析下Handler及HandlerThread,畢竟Handler在Android開發和麵試中都是涉及很多的知識點,所以很有必要全方位的瞭解透徹。
作用:
我們都知道在Android中,主執行緒(UI執行緒)需要高響應,不能做耗時操作(不然會出現ANR異常),所以一般的耗時的操作必須只能在子執行緒,比如在非同步任務中進行網路請求,然後如何通知更新介面呢?這就是Handler的作用,即 在不同執行緒間進行通訊
使用:
handler一般有兩種使用方式
1 post方式
public class HandlerDemoActivity extends AppCompatActivity {
private Handler mHandler = new Handler();//Handler物件
private Runnable mRunnable;//在主執行緒中執行的Runnable物件
private TextView tv_show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_demo);
tv_show= (TextView) findViewById(R.id.tv_show);
tv_show.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doTask();
}
});
}
private void doTask() {
new Thread(new Runnable() {
@Override
public void run() {
Log.i("wy 1",Thread.currentThread().getName());
try {
Thread.sleep(5000);
mRunnable = new Runnable() {
@Override
public void run() {
tv_show.setText("5s");
Log.i("wy 2",Thread.currentThread().getName());
}
};
} catch (InterruptedException e) {
e.printStackTrace();
}
mHandler.post(mRunnable);
}
}).start();
}
}
可以看到,Handler.post(Runnable)執行在子執行緒中,但是Runnable中的Run方法會執行在主執行緒中,所以可以在其中更新UI,也即實現了從子執行緒到主執行緒的資訊傳遞。可以列印Log觀察執行緒。
2 sendMessage方式
public class HandlerDemo2Activity extends AppCompatActivity {
private Handler mHandler =new Handler(){
//重寫該方法,所有的send到的msg都會在該方法中處理,UI執行緒
@Override
public void handleMessage(Message msg) {
Log.i("wy 2",Thread.currentThread().getName());
switch (msg.what){
case 1:
tv_show.setText("5s");
break;
default:
break;
}
}
};
private TextView tv_show;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_demo);
tv_show= (TextView) findViewById(R.id.tv_show);
tv_show.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
doTask();
}
});
}
private void doTask() {
new Thread(new Runnable() {
@Override
public void run() {
Log.i("wy 1",Thread.currentThread().getName());
Message message=Message.obtain();
message.what=1;
mHandler.sendMessage(message);
}
}).start();
}
}
可以看到,這種寫法更加直觀一些,在子執行緒中,通過handler呼叫sendMessage方法,然後在重寫的HandlerMessage方法中進行處理,該方法是UI執行緒,自動實現了子執行緒和主執行緒的切換。
同時,post和sendMessage還有很多延伸出來的方法,原理類似,不做闡述了。
原理:
為什麼Handler可以實現不同執行緒間的通訊,內部的機制到底是如何實現的呢?帶著這樣的疑問,我們來看一張圖片:
可以看到涉及到四個類,分別為:Looper,MessageQueue,Message和Handler
直觀上它們的關係可以理解為:Handler傳送訊息Message到MessageQueue佇列中,Looper在MessageQueue中取Message然後傳送到Handler中進行處理。我們從原始碼角度分析一波
原始碼分析:
Looper:
看看該類的結構圖:
可以發現,該類十分簡單,6個屬性,十幾個方法,重要的不超過十個,Looper使用一般是呼叫prepare和loop方法,我們來看看其內部是如何:
1 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));
}
可以發現,prepare帶參的方法中,sThreadLocal是用來儲存建立的Looper物件,且判斷保證了該物件的唯一性
2 構造方法:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
構造方法生成一個MessageQueue物件和獲得當前執行緒物件,此時,該Looper和MessageQueue有了關聯
3 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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
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.recycleUnchecked();
}
}
loop方法可以分成兩部分分析
1. 獲取looper物件,MessageQueue物件,和一些簡單判斷
2. 一個for迴圈不斷從MessageQueue中獲取Message然後呼叫dispatchMessage分發出去。
剩下的其他prepareMainLooper,getMainLooper,myLooper,myQueue,isCurrentThread都是些簡單的get,set方法,不作分析。該類是不是很簡單呢?
Looper主要作用:
1、 與當前執行緒繫結,保證一個執行緒只會有一個Looper例項,同時一個Looper例項也只有一個MessageQueue。
2、 loop()方法,不斷從MessageQueue中去取訊息,交給訊息的target屬性的dispatchMessage去處理。
MessageQueue就是一個訊息佇列,更多的是add,get,remove作用,裡面用到了很多native方法,不作分析
Handler:
我們從Handler的使用中一步步深入下去:
1 構造方法:
public Handler() {
this(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;
}
構造方法主要是獲取了Looper,MessageQueue等物件,等一下,這裡的Looper物件是那裡生成的呢?實際上,因為實在主執行緒中,在Activity的啟動類ActivityThread中,主動呼叫了prepare和loop方法,即Looper.prepareMainLooper()和Looper.loop();
2 sendMessage:
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);
}
一步步跟下去發現最終呼叫的是 enqueueMessage方法,其中
msg.target=this 將Handler物件賦值給了msg.target,Looper的loop方法中 msg.target.dispatchMessage(msg)就是在此時賦值的。
queue.enqueueMessage(msg, uptimeMillis)將msg加入到MessageQueue佇列中了。
3 dispatchMessage:
loop中的dispatchMessage將訊息分發:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
發現最終呼叫的就是handlerMessage,也就是我們需要重寫的方法。
總結一下
1、首先Looper.prepare()在本執行緒中儲存一個Looper例項,然後該例項中儲存一個MessageQueue物件;因為Looper.prepare()在一個執行緒中只能呼叫一次,所以MessageQueue在一個執行緒中只會存在一個。
2、Looper.loop()會讓當前執行緒進入一個無限迴圈,不端從MessageQueue的例項中讀取訊息,然後回撥msg.target.dispatchMessage(msg)方法。
3、Handler的構造方法,會首先得到當前執行緒中儲存的Looper例項,進而與Looper例項中的MessageQueue想關聯。
4、Handler的sendMessage方法,會給msg的target賦值為handler自身,然後加入MessageQueue中。
5、在構造Handler例項時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終呼叫的方法。
3 post(Runnable)方式
程式碼一步一步跟下去,可以發現:
1.
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
2.
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
3.
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
。。。
和上面一樣,只是通過一個簡單的轉化,即將runnable物件賦值為message的callback屬性,然後也是呼叫sendMessage方法,同時,我們可以發現,最終呼叫的dispatchMessage中:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
對callback的判斷就是對於post方式的處理。可以稱之為殊途同歸!
HandlerThread:
我們都知道Handler是用來執行緒間進行通訊的機制,一般我們使用的方式都是預設建立在主執行緒中,那麼在子執行緒中如何使用呢?
使用:
1.建立一個HandlerThread,即建立了一個包含Looper的執行緒。
建立HandlerThread後一定要記得start()
HandlerThread handlerThread = new HandlerThread("xixiancheng");
handlerThread.start();
2.獲取HandlerThread的Looper
Looper looper = handlerThread.getLooper();
3.建Handler,通過Looper初始化
將該子執行緒中的loop物件繫結到handler中,此時的handler就是該子執行緒的handler,可以在main中呼叫傳送訊息。
Handler handler = new Handler(looper);
通過以上三步我們就成功建立HandlerThread。通過handler傳送訊息,就會在子執行緒中執行。
如果想讓HandlerThread退出,則需要呼叫handlerThread.quit();。
此時可以實現不同執行緒之間進行訊息的傳遞了。
原始碼分析:
1.
HandlerThread handlerThread = new HandlerThread("xixiancheng");
handlerThread.start();
可以看到,HandlerThread是Thread類,我們來看其run方法:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
Looper在該子執行緒中建立,所以當呼叫我們的new Handler(looper)方法時,該Handler也就在對應於該子執行緒中,HandlerThread原始碼中主要就是這個run方法,如果我們自己想在子執行緒中建立Handler物件也可以模仿其寫法。我們熟知的IntentServie內部就是HandlerThread來實現的。
好了,關於Handler的使用知識點大概就這些了。歡迎閱讀其他的文章: