版權聲明:本文爲博主原創文章,未經博主容許不得轉載
源碼:github.com/AnliaLee
你們要是看到有錯誤的地方或者有啥好的建議,歡迎留言評論java
在Android中規定了修改UI控件,更新視圖這些操做必須在UI線程(主線程)中進行。而一些耗時的操做例如加載網絡數據,查詢本地文件、數據等,則必須放到子線程中。所以咱們須要一種通訊機制使得子線程完成任務後能夠通知UI線程更新界面。本章將挑選線程通訊機制中的Handler進行講解,聊一聊它和ThreadLocal、Message、MessageQueue以及Looper之間的故事android
ps:本篇博客主要是起到一個引導的做用,幫助你們梳理清楚Handler、Looper、MessageQueue等角色的關係,以及它們在Handler消息機制下所起到的做用,並不會過多地深刻到源碼中。至於源碼的講解,網上優秀的文章實在是太多了,這裏推薦幾位前輩撰寫的博客,你們能夠相互對照着看看git
ps2:看完這篇博客再去了解源碼有助於消化知識哦~github
往期回顧
大話Android多線程(一) Thread和Runnable的聯繫和區別
大話Android多線程(二) synchronized使用解析安全
在遙遠的Android大陸中,U國(UI線程,即主線程)和T國(Thread,子線程)之間發生了戰爭。某日,U國部隊準備攻打T國的首都R城,只要收到地下組織(Handler)的特工小h(Handler的實例)的信號後,便可採起相應的行動(更新UI)。因爲R城戒備森嚴,小h傳達信號須要作到格外隱祕,所以制定了以下計劃,計劃由小h和他專屬的情報員小L(Looper,Handler在建立時就會關聯一個Looper對象,而Looper存放在ThreadLocal中,每個線程都會維護本身的Looper,這裏的Looper天然是屬於主線程的)執行:網絡
建立Handler實例時,重寫handleMessage方法(在其中編寫更新UI的操做),以便在消息分配後執行數據結構
public class HandlerTestActivity extends AppCompatActivity {
TextView textShow;
private static final int CODE_TEST_ONE = 101;
private static final int CODE_TEST_TWO = 102;
private static final int CODE_TEST_THREE = 103;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
textShow = (TextView) findViewById(R.id.text_show);
}
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case CODE_TEST_ONE:
textShow.setText("開始刺探軍情...");
break;
case CODE_TEST_TWO:
textShow.setText("情報收集完畢...");
break;
case CODE_TEST_THREE:
textShow.setText("發起總攻!");
break;
}
}
};
}
複製代碼
MessageQueue:經過單鏈表的數據結構來存儲消息列表,消息按照先進先出的原則進行存取,在線程中建立一個Looper實例時,會自動建立一個與之配對的MessageQueue多線程
咱們在子線程中使用Handler實例發送消息時,Handler會調用內部方法enqueueMessage將Message插入到MessageQueue中(Handler.enqueueMessage方法最後調用了MessageQueue.enqueueMessage方法存放Message,有關Handler發送消息的方法請見下文附錄一)app
public class HandlerTestActivity extends AppCompatActivity {
//省略部分代碼...
public void clickEvent(View view) {
switch (view.getId()) {
case R.id.btn_start:
new Thread(new TestRunnable()).start();
break;
}
}
private class TestRunnable implements Runnable{
@Override
public void run() {
try {
handler.sendEmptyMessage(CODE_TEST_ONE);
// 你也能夠這樣發送消息
// Message message = Message.obtain();
// message.what = CODE_TEST_ONE;
// handler.sendMessage(message);
// 或者
// message.sendToTarget();
Thread.sleep(2000);
handler.sendEmptyMessage(CODE_TEST_TWO);
Thread.sleep(2000);
handler.sendEmptyMessage(CODE_TEST_THREE);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
複製代碼
Looper.loop方法不斷地調用MessageQueue.next方法讀取消息,若消息不爲空則調用Handler.dispatchMessage方法將消息分發出去異步
以前在Looper中調用了Handler的dispatchMessage方法,而在dispatchMessage方法中又調用了Handler.handleMessage方法,這樣就回到了咱們第一點重寫的代碼,實現了從子線程中發送消息到主線程更新UI的操做
最後運行效果如圖所示
總結Handler建立,發送消息處處理消息的整個流程,大體以下圖所示
主線程的Looper在應用開啓前系統就已經幫咱們建立好了,若是咱們要在主線程中向子線程發送消息,則須要在子線程建立時手動建立Looper並開啓循環,具體實現代碼以下:
public class HandlerTestActivity extends AppCompatActivity {
private Handler handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
TestThread testThread = new TestThread();
testThread.start();
while (true){//保證testThread.looper已經初始化
if(testThread.looper!=null){
handler2 = new Handler(testThread.looper){
@Override
public void handleMessage(Message msg) {//子線程收到消息後執行
switch (msg.what){
case CODE_TEST_FOUR:
Log.e(TAG,"收到主線程發送的消息");
break;
}
}
};
handler2.sendEmptyMessage(CODE_TEST_FOUR);//在主線程中發送消息
break;
}
}
private class TestThread extends Thread{
private Looper looper;
@Override
public void run() {
super.run();
Looper.prepare();//建立該子線程的Looper實例
looper = Looper.myLooper();//取出該子線程的Looper實例
Looper.loop();//開始循環
}
}
}
複製代碼
固然上面的代碼只是簡單地體驗一下手動建立Looper的過程,實際上系統已經爲咱們封裝好了HandlerThread類,它幫助咱們完成了建立Looper、開啓循環等一系列操做,所以使用HandlerThread會更加方便和安全。以上述一樣的操做爲例,此次咱們直接繼承HandlerThread建立子線程:
public class HandlerTestActivity extends AppCompatActivity {
private HandlerThread handlerThread;
private Handler handler2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
handler2 = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {//子線程收到消息後執行
switch (msg.what){
case CODE_TEST_FOUR:
Log.e(TAG,"收到主線程發送的消息");
break;
}
}
};
handler2.sendEmptyMessage(CODE_TEST_FOUR);//在主線程中發送消息
}
@Override
protected void onDestroy() {
super.onDestroy();
handlerThread.quit();
}
}
複製代碼
有關HandlerThread更詳細的資料你們能夠看這篇博客
子線程向子線程發送消息的過程和以前講的差很少,就不贅述了
protected void onCreate(Bundle savedInstanceState) {
//省略部分代碼...
handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
handler2 = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {//子線程收到消息後執行
switch (msg.what){
case CODE_TEST_FOUR:
Log.e(TAG,"收到另外一個子線程發送的消息");
break;
}
}
};
Thread testThread = new Thread(new Runnable() {
@Override
public void run() {
handler2.sendEmptyMessage(CODE_TEST_FOUR);//在另外一個子線程中發送消息
}
});
testThread.start();
}
複製代碼
Handler發送消息多種方法,但不管咱們使用哪一種方法,其最終都是利用MessageQueue.enqueueMessage方法將消息插入到消息隊列中。各類方法內部的執行順序以下圖所示,咱們能夠從紅框內任意一步出發,只需注意該方法的做用及傳入參數的區別便可