默認(rèn)情況下,ActivityThread類為我們創(chuàng)建的了主線程的Looper和消息隊列赴叹,所以當(dāng)你創(chuàng)建Handler之后發(fā)送消息的時候鸿染,消息的輪訓(xùn)和handle都是在ui線程進(jìn)行的。這種情況屬于子線程給主線程發(fā)消息乞巧,通知主線程更新ui...等牡昆,那么反過來,怎么才能讓主線程給子線程發(fā)消息摊欠,通知子線程做一些耗時邏輯丢烘??
Android的消息機(jī)制遵循三個步驟:
1 創(chuàng)建當(dāng)前線程的Looper
2 創(chuàng)建當(dāng)前線程的Handler
3 調(diào)用當(dāng)前線程Looper對象的loop方法
那么如果我想創(chuàng)建非主線程的Handler并且發(fā)送消息些椒、處理消息播瞳,這一系列的操作我們應(yīng)該怎么辦?直接上代碼:
public class ChildThreadHandlerActivity extends Activity {
private MyThread childThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
childThread = new MyThread();
childThread.start();
Handler childHandler = new Handler(childThread.childLooper){//這樣之后免糕,childHandler和childLooper就關(guān)聯(lián)起來了赢乓。
public void handleMessage(Message msg) {
};
};
}
private class MyThread extends Thread{
public Looper childLooper;
@Override
public void run() {
Looper.prepare();//創(chuàng)建與當(dāng)前線程相關(guān)的Looper
childLooper = Looper.myLooper();//獲取當(dāng)前線程的Looper對象
Looper.loop();//調(diào)用此方法,消息才會循環(huán)處理
}
}
}
代碼如上石窑,我們依然循序Android的三步走戰(zhàn)略牌芋,完成了子線程Handler的創(chuàng)建,難道這樣創(chuàng)建完了松逊,就可以發(fā)消息了么躺屁?發(fā)的消息在什么線程處理?一系列的問題经宏,怎么辦犀暑?看代碼Q被鳌!耐亏!運(yùn)行上述代碼徊都,我們發(fā)現(xiàn)一個問題,就是此代碼一會崩潰广辰、一會不崩潰暇矫,通過查看日志我們看到崩潰的原因是空指針。誰為空择吊?李根??查到是我們的Looper對象干发,怎么會那朱巨?我不是在子線程的run方法中初始化Looper對象了么史翘?話是沒錯枉长,但是你要知道,當(dāng)你statr子線程的時候琼讽,雖然子線程的run方法得到執(zhí)行必峰,但是主線程中代碼依然會向下執(zhí)行,造成空指針的原因是當(dāng)我們new Handler(childThread.childLooper)的時候钻蹬,run方法中的Looper對象還沒初始化吼蚁。當(dāng)然這種情況是隨機(jī)的,所以造成偶現(xiàn)的崩潰问欠。
那怎么辦肝匆?難道我們不能創(chuàng)建子線程Handler ?顺献?旗国?No!!!No!!!No!!!,你能想到的Android早就為我們實(shí)現(xiàn)好了注整,HandlerThread類就是解決這個問題的關(guān)鍵所在能曾,看代碼!V坠臁寿冕!
public class HandlerThreadActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
TextView textView = (TextView) findViewById(R.id.tv);
textView.setText("HandlerThreadActivity.class");
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
Handler mHandler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("HandlerThreadActivity.class","uiThread2------"+Thread.currentThread());//子線程
}
};
Log.d("HandlerThreadActivity.class","uiThread1------"+Thread.currentThread());//主線程
mHandler.sendEmptyMessage(1);
}
}
創(chuàng)建HandlerThread對象的時候,有個參數(shù)椒袍,是指定線程名字的驼唱。上面的代碼不管運(yùn)行多少次都不會奔潰!>允睢曙蒸!并且這種方法創(chuàng)建的handler的handleMessage方法運(yùn)行在子線程中捌治。所以我們可以在這里處理一些耗時的邏輯。到此我們完成了主線程給子線程發(fā)通知纽窟,在子線程做耗時邏輯的操作肖油。
下面我們?nèi)タ纯丛创a,看看為什么使用HandlerThread就可以避免空指針那臂港?
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;
}
HandlerThread類的getLooper方法如上森枪,我們看到當(dāng)我們獲取當(dāng)前線程Looper對象的時候,會先判斷當(dāng)前線程是否存活审孽,然后還要判斷Looper對象是否為空县袱,都滿足之后才會返回給我Looper對象,否則處于等待狀態(tài)S恿Α式散!既然有等待,那就有喚醒的時候打颤,在那里那暴拄??编饺?我們發(fā)現(xiàn)HandlerThread的run方法中乖篷,有如下代碼:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
說明了什么那?透且?撕蔼?HandlerThread類start的時候,Looper對象就初始化了秽誊,并喚醒之前等待的鲸沮。所以HandlerThread很好的避免了之前空指針的產(chǎn)生。所以以后要想創(chuàng)建非主線程的Handler時锅论,我們用HandlerThread類提供的Looper對象即可讼溺。