上一篇我介紹了Handler機制的工作原理,默認情況下,ActivityThread類為我們創建的了主線程的Looper和消息隊列,所以當你創建Handler之后發送消息的時候,消息的輪訓和handle都是在ui線程進行的。這種情況屬於子線程給主線程發消息,通知主線程更新ui...等,那么反過來,怎么才能讓主線程給子線程發消息,通知子線程做一些耗時邏輯??
之前的學習我們知道,Android的消息機制遵循三個步驟:
1 創建當前線程的Looper
2 創建當前線程的Handler
3 調用當前線程Looper對象的loop方法
看過之前文章的朋友會注意到,本篇我特意強調了“當前線程”。是的之前我們學習的很多都是Android未我們做好了的,譬如:創建主線程的Looper、主線程的消息隊列...就連我們使用的handler也是主線程的。那么如果我想創建非主線程的Handler並且發送消息、處理消息,這一系列的操作我們應該怎么辦那???不怎么辦、涼拌~~~什么意思???依葫蘆畫瓢,依然遵循上面的三步走,直接上代碼!!!!
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就關聯起來了。 public void handleMessage(Message msg) { }; }; } private class MyThread extends Thread{ public Looper childLooper; @Override public void run() { Looper.prepare();//創建與當前線程相關的Looper childLooper = Looper.myLooper();//獲取當前線程的Looper對象 Looper.loop();//調用此方法,消息才會循環處理 } } }
代碼如上,我們依然循序Android的三步走戰略,完成了子線程Handler的創建,難道這樣創建完了,就可以發消息了么?發的消息在什么線程處理?一系列的問題,怎么辦?看代碼!!!運行上述代碼,我們發現一個問題,就是此代碼一會崩潰、一會不崩潰,通過查看日志我們看到崩潰的原因是空指針。誰為空???查到是我們的Looper對象,怎么會那?我不是在子線程的run方法中初始化Looper對象了么?話是沒錯,但是你要知道,當你statr子線程的時候,雖然子線程的run方法得到執行,但是主線程中代碼依然會向下執行,造成空指針的原因是當我們new Handler(childThread.childLooper)的時候,run方法中的Looper對象還沒初始化。當然這種情況是隨機的,所以造成偶現的崩潰。
那怎么辦?難道我們不能創建子線程Handler ???No!!!No!!!No!!!,你能想到的Android早就為我們實現好了,HandlerThread類就是解決這個問題的關鍵所在,看代碼!!!
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); } }
創建HandlerThread對象的時候,有個參數,是指定線程名字的。上面的代碼不管運行多少次都不會奔潰!!!並且這種方法創建的handler的handleMessage方法運行在子線程中。所以我們可以在這里處理一些耗時的邏輯。到此我們完成了主線程給子線程發通知,在子線程做耗時邏輯的操作。
下面我們去看看源碼,看看為什么使用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方法如上,我們看到當我們獲取當前線程Looper對象的時候,會先判斷當前線程是否存活,然后還要判斷Looper對象是否為空,都滿足之后才會返回給我Looper對象,否則處於等待狀態!!既然有等待,那就有喚醒的時候,在那里那???我們發現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很好的避免了之前空指針的產生。所以以后要想創建非主線程的Handler時,我們用HandlerThread類提供的Looper對象即可。
至此,前三篇我們講了Handler的使用、工作原理、創建子線程Handler。下一篇我會講使用Handler引起的內存泄漏的解決辦法。