Android-異步消息處理機(jī)制2以及HandlerThread的介紹
之間在上篇文章中介紹過(guò)了Android的異步消息處理機(jī)制,這里再進(jìn)行一些補(bǔ)充,說(shuō)下多線程中的消息處理以及HandlerThread的介紹,如果你還不是很了解handler的機(jī)制,可以先看看上篇文章Android-異步消息處理機(jī)制(Handler,Looper,Message)
在上篇文章中開(kāi)頭說(shuō)過(guò)我們平時(shí)在子線程中去更新ui的操作一般是通過(guò)handler來(lái)發(fā)送message的方式進(jìn)行處理,但是其實(shí)還有幾種更簡(jiǎn)便的方式來(lái)進(jìn)行ui的操作:
Handler的post()方法
View的post()方法
Activity的runOnUiThread()方法
代碼如下:
new Thread(new Runnable() {
@Override
public void run() {
//1.普遍的方式
//發(fā)送消息
Message msg = Message.obtain();
msg.what = 111;
Bundle bundle=new Bundle();
bundle.putString("huan","hello");
msg.setData(bundle);
mHandler.sendMessage(msg);
//2.post的方式
mHandler.post(new Runnable() {
@Override
public void run() {
//可以直接在這兒進(jìn)行ui的更新
}
});
//3.view.post方式
mTvHelloWord.post(new Runnable() {
@Override
public void run() {
//可以直接在這兒進(jìn)行ui的更新
}
});
//4.runOnUiThread方式
runOnUiThread(new Runnable() {
@Override
public void run() {
//可以直接在這兒進(jìn)行ui的更新
}
});
}
}).start();
從代碼中可以明顯的看出,后面三種方式要簡(jiǎn)便的多,而且是一種比一種簡(jiǎn)便,都不需要通過(guò)Message 的創(chuàng)建去封裝消息;第二種方式還需要使用handler作為載體,第三種方式需要使用view來(lái)作為載體,第四種方式直接用runOnUiThread方法即可。為什么可以這樣寫(xiě)也能達(dá)到目的?進(jìn)去看看知道了。
1.先說(shuō)下handler post方式的使用,我們不需要再去創(chuàng)建一個(gè)Message對(duì)象,把數(shù)據(jù)封裝在Message中,然后在handler的callback中進(jìn)行ui的操作,而是直接可以在run中進(jìn)行操作。進(jìn)入到post方法中:
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
所噶,原來(lái)還是通過(guò)sendMessageDelayed(Message msg, long delayMillis)這個(gè)方法來(lái)進(jìn)行操作的,并且將我們傳入的runnable對(duì)象通過(guò)getPostMessage方法轉(zhuǎn)為了一個(gè)Message;那我們?cè)龠M(jìn)入getPostMessage方法中一看應(yīng)該就能明了了:
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
創(chuàng)建了一個(gè)消息,并把這個(gè)消息的callback設(shè)置為了我們傳入的runnnable;還記得我們上篇文章說(shuō)過(guò)的在Looper進(jìn)行消息循環(huán)的時(shí)候,取出來(lái)的消息將會(huì)通過(guò)dispatchMessage這個(gè)方法進(jìn)行處理:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先判斷的就是msg的callback是否為null,如果不為null就會(huì)執(zhí)行handleCallback(msg):
private static void handleCallback(Message message) {
message.callback.run();
}
直接調(diào)用了msg的runnable對(duì)象也就是我們一開(kāi)始傳給msg的runnable對(duì)象,瞬間就很清晰明了了。
2.view.post方式
view.post方法和handler的寫(xiě)法幾乎一致,看看它的里面實(shí)現(xiàn)了什么:
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
// Assume that post will succeed later
ViewRootImpl.getRunQueue().post(action);
return true;
}
原來(lái)就是通過(guò)attachInfo獲取到了handler,然后執(zhí)行handler的post方法而已。換湯不換藥。
3.runOnUiThread方式
這個(gè)方法其實(shí)是屬于Activity的方法,也就是只能在Activity中才能使用,代碼如下:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
首先判斷了當(dāng)前線程是否是ui線程,如果不是,就用handler post的方式;否則就直接run。
看完了這三種方式的源代碼發(fā)現(xiàn)其實(shí)底層都是通過(guò)handler發(fā)送message 的方式來(lái)進(jìn)行消息的處理;所以平時(shí)我們?cè)陂_(kāi)發(fā)android的過(guò)程中如果明白了handler的消息機(jī)制;那么這幾種方法你都是可以任意使用的,哪種方便用哪種。
Looper和handler的同步關(guān)系:
接下來(lái)說(shuō)下在多線程中handler和looper的處理;先模擬一個(gè)運(yùn)行場(chǎng)景:
- 主線程中開(kāi)啟一個(gè)線程1;線程1開(kāi)啟的時(shí)候會(huì)啟動(dòng)線程2;
- 線程2啟動(dòng)后獲取到Looper,并執(zhí)行l(wèi)ooper.loop ;
- 將線程2中的looper對(duì)象賦值給線程1中的looper對(duì)象
- 通過(guò)線程1中的looper對(duì)象,在主線程中創(chuàng)建一個(gè)hanlder對(duì)象;
- 通過(guò)handler發(fā)送一個(gè)msg,請(qǐng)問(wèn)這個(gè)msg是在哪個(gè)線程中收到這個(gè)消息并進(jìn)行處理;
看下代碼:
線程1:
public class LooperThread1 extends Thread {
/**
* 定義一個(gè)public的成員變量Looper
*/
public Looper myLooper = null;
/**
* 初始化Looper,開(kāi)啟消息循環(huán)
*/
public void initLooper(){
Looper.prepare();
myLooper = Looper.myLooper();
Looper.loop();
}
@Override
public void run() {
//開(kāi)啟另一個(gè)線程
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
initLooper();
}
});
thread2.start();
Log.e("test", "thread2 id:" + thread2.getId());
}
}
主線程:
//主線程id
Log.e("test", "Main Thread:" + Thread.currentThread().getId());
LooperThread1 myLooperThread1=new LooperThread1();
myLooperThread1.start();
//線程1 id
Log.e("test","thread1 id:"+myLooperThread1.getId());
Looper looper=myLooperThread1.myLooper;
android.os.Handler handler=new android.os.Handler(looper, new android.os.Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if(msg.what==111){
Log.e("test","handleMessage :111");
//當(dāng)前線程
Log.e("test","current Thread:"+Thread.currentThread().getId());
}
return false;
}
});
handler.sendEmptyMessage(111);
最開(kāi)始我是為了驗(yàn)證這個(gè)handler發(fā)送的消息最終是在哪個(gè)線程中接收,但是其實(shí)這段代碼是有問(wèn)題的,一運(yùn)行會(huì)報(bào)java.lang.NullPointerException。具體的原因就在與:創(chuàng)建handler的時(shí)候,傳入的這個(gè)looper為空。我們?cè)讷@取線程1的looper對(duì)象時(shí),這個(gè)時(shí)候可能線程2還沒(méi)有執(zhí)行完獲取到looper;所以問(wèn)題就出在這兒;不過(guò)我們可以加一個(gè)同步鎖的方式來(lái)解決,但是如果每次寫(xiě)你都去這樣操作那不是很麻煩? 所以,android給我們推薦了一個(gè) HandlerThread線程來(lái)解決多線程中handler和looper之間同步的問(wèn)題。
HandlerThread中的源碼:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason is isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
其實(shí)就是加上了一個(gè)同步鎖的機(jī)制,并且在run中自動(dòng)幫我們創(chuàng)建好了looper;那么我們接下來(lái)再來(lái)驗(yàn)證下消息的處理是在哪個(gè)線程:
//主線程id
Log.e("test", "Main Thread:" + Thread.currentThread().getId());
HandlerThread handlerThread = new HandlerThread("handler_thread");
handlerThread.start();
Log.e("test", "HandlerThread:" + handlerThread.getId());
android.os.Handler handler=new android.os.Handler(handlerThread.getLooper(), new android.os.Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if(msg.what==222){
Log.e("test","receiveMsh thread id:"+Thread.currentThread().getId());
}
return false;
}
});
handler.sendEmptyMessage(222);
打印log為:
得出結(jié)論:handler發(fā)送的Message處理,不會(huì)依賴與創(chuàng)建handler時(shí)所在的線程,依賴與傳入的looper對(duì)象所在的線程,looper所在的線程來(lái)執(zhí)行msg的處理;
到這里,通過(guò)兩篇文章總算是將handler這個(gè)知識(shí)點(diǎn)大部分總結(jié)完了;接下來(lái)會(huì)繼續(xù)總結(jié)其他技術(shù)點(diǎn)的知識(shí);如果文章有疏漏,錯(cuò)誤,望君能給我評(píng)論指出。謝謝!