0,寫在前面
博主在學(xué)習(xí)Handler消息傳遞機制時驶兜,學(xué)習(xí)的方法是通過問題來驅(qū)動理解扼仲,都是關(guān)注度比較高的問題,以下是我在學(xué)習(xí)后的簡單整理抄淑,歡迎吐槽屠凶!
1,Handler是什么肆资?能干嘛矗愧?
Google的Android開發(fā)文檔是這么寫的:
A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
簡言之,就是android.os.Handler允許我們發(fā)送和處理與線程的MessageQueue相關(guān)的Message和Runnable對象郑原。 每個Handler實例都與單個線程和該線程的消息隊列相關(guān)聯(lián)唉韭。
主要用于:1)消息創(chuàng)建;2)將消息插入到隊列中犯犁;3)處理消費者線程上的消息属愤;4)管理隊列中的消息。
2酸役,為什么需要Handler住诸?
這張圖片清晰的說明了谷歌工程師為什么要開發(fā)Handler這個類』猎瑁可以看出贱呐,Google工程師開發(fā)它主要是為了解決在非UI線程中更新UI組件比較麻煩的問題。
3入桂,Handler都有哪些特點奄薇?
handler可以分發(fā)Message對象和Runnable對象到主線程中, 每個Handler實例抗愁,都會綁定到創(chuàng)建他的線程中(一般是位于主線程)惕艳,它有兩個作用:
1)安排消息或Runnable 在某個主線程中某個地方執(zhí)行;
2)安排一個動作在不同的線程中執(zhí)行驹愚。
Handler中分發(fā)消息的一些方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message劣纲,long)
sendMessageDelayed(Message逢捺,long)
以上post類方法允許你排列一個Runnable對象到主線程隊列中,
sendMessage類方法癞季, 允許你安排一個帶數(shù)據(jù)的Message對象到隊列中劫瞳,等待更新倘潜。
4,Handler執(zhí)行的流程圖
UI線程:就是主線程,系統(tǒng)在創(chuàng)建UI線程的時候會初始化一個Looper對象,同時也會 創(chuàng)建一個與其關(guān)聯(lián)的MessageQueue;
Handler:作用就是發(fā)送與處理信息,如果希望Handler正常工作,在當前線程中要有一個Looper對象
Message:Handler接收與處理的消息對象
MessageQueue:消息隊列,先進先出管理Message,在初始化Looper對象時會創(chuàng)建一個與之關(guān)聯(lián)的MessageQueue;
Looper:每個線程只能夠有一個Looper,管理MessageQueue,不斷地從中取出Message分發(fā)給對應(yīng)的Handler處理志于!
大白話來說涮因,就是當子線程想修改Activity中的UI組件時,我們可以新建一個Handler對象,通過這個對象向主線程發(fā)送信息;而發(fā)送的信息會先到主線程的MessageQueue進行等待,由Looper按先入先出順序取出,再根據(jù)message對象的what屬性分發(fā)給對應(yīng)的Handler進行處理。
5伺绽,如何理解Handler消息處理機制养泡?
消息處理機制本質(zhì):一個線程開啟循環(huán)模式持續(xù)監(jiān)聽并依次處理其他線程給它發(fā)的消息。
簡單來說奈应,就是一個線程開啟一個無限循環(huán)模式澜掩,不斷遍歷自己的消息列表,如果有消息就挨個拿出來做處理杖挣,如果列表沒消息肩榕,自己就堵塞(相當于wait,讓出cpu資源給其他線程)惩妇,其他線程如果想讓該線程做什么事株汉,就往該線程的消息隊列插入消息,該線程會不斷從隊列里拿出消息做處理歌殃。
6乔妈,Looper、Handler挺份、MessageQueue,Message作用和存在的意義褒翰?
6.1,Looper
我們知道一個線程是一段可執(zhí)行的代碼匀泊,當可執(zhí)行代碼執(zhí)行完成后优训,線程生命周期便會終止,線程就會退出各聘,那么做為App的主線程揣非,如果代碼段執(zhí)行完了會怎樣?躲因,那么就會出現(xiàn)App啟動后執(zhí)行一段代碼后就自動退出了早敬,這是很不合理的。所以為了防止代碼段被執(zhí)行完大脉,只能在代碼中插入一個死循環(huán)搞监,那么代碼就不會被執(zhí)行完,然后自動退出镰矿,怎么在在代碼中插入一個死循環(huán)呢琐驴?那么Looper出現(xiàn)了,在主線程中調(diào)用Looper.prepare()...Looper.loop()就會變當前線程變成Looper線程(可以先簡單理解:無限循環(huán)不退出的線程),Looper.loop()方法里面有一段死循環(huán)的代碼绝淡,所以主線程會進入while(true){...}的代碼段跳不出來宙刘,但是主線程也不能什么都不做吧?其實所有做的事情都在while(true){...}里面做了牢酵,主線程會在死循環(huán)中不斷等其他線程給它發(fā)消息(消息包括:Activity啟動悬包,生命周期,更新UI馍乙,控件事件等)布近,一有消息就根據(jù)消息做相應(yīng)的處理,Looper的另外一部分工作就是在循環(huán)代碼中會不斷從消息隊列挨個拿出消息給主線程處理潘拨。
6.2吊输,MessageQueue
MessageQueue 存在的原因很簡單,就是同一線程在同一時間只能處理一個消息铁追,同一線程代碼執(zhí)行是不具有并發(fā)性季蚂,所以需要隊列來保存消息和安排每個消息的處理順序。多個其他線程往UI線程發(fā)送消息琅束,UI線程必須把這些消息保持到一個列表(它同一時間不能處理那么多任務(wù)),然后挨個拿出來處理扭屁,這種設(shè)計很簡單,我們平時寫代碼其實也經(jīng)常這么做涩禀。每一個Looper線程都會維護這樣一個隊列料滥,而且僅此一個,這個隊列的消息只能由該線程處理艾船。
6.3葵腹,Message
想讓主線程做什么事,總要告訴它吧屿岂,總要傳遞點數(shù)據(jù)給它吧践宴,Message就是這個載體。
6.4爷怀,Handler
簡單說Handler用于同一個進程的線程間通信阻肩。Looper讓主線程無限循環(huán)地從自己的MessageQueue拿出消息處理,既然這樣我們就知道處理消息肯定是在主線程中處理的运授,那么怎樣在其他的線程往主線程的隊列里放入消息呢烤惊?其實很簡單,我們知道在同一進程中線程和線程之間資源是共享的吁朦,也就是對于任何變量在任何線程都是可以訪問和修改的柒室,只要考慮并發(fā)性做好同步就行了,那么只要拿到MessageQueue 的實例逗宜,就可以往主線程的MessageQueue放入消息雄右,主線程在輪詢的時候就會在主線程處理這個消息剥啤。那么怎么拿到主線程 MessageQueue的實例,是可以拿到的(在主線程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),但是Google 為了統(tǒng)一添加消息和消息的回調(diào)處理不脯,又專門構(gòu)建了Handler類,你只要在主線程構(gòu)建Handler類刻诊,那么這個Handler實例就獲取主線程MessageQueue實例的引用(獲取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;)防楷,Handler 在sendMessage的時候就通過這個引用往消息隊列里插入新消息。Handler 的另外一個作用则涯,就是能統(tǒng)一處理消息的回調(diào)复局。這樣一個Handler發(fā)出消息又確保消息處理也是自己來做,這樣的設(shè)計非常的贊粟判。具體做法就是在隊列里面的Message持有Handler的引用(哪個handler 把它放到隊列里亿昏,message就持有了這個handler的引用),然后等到主線程輪詢到這個message的時候档礁,就來回調(diào)我們經(jīng)常重寫的Handler的handleMessage(Message msg)方法角钩。
7,Handler的用法
7.1呻澜,消息的創(chuàng)建
Message obtainMessage(int what, int arg1, int arg2)递礼;
Message obtainMessage();
Message obtainMessage(int what, int arg1, int arg2, Object obj)羹幸;
Message obtainMessage(int what)脊髓;
Message obtainMessage(int what, Object obj);
7.2栅受,將消息插入到隊列中
7.2.1将硝,添加任務(wù)到消息隊列中
Message obtainMessage(int what, int arg1, int arg2)
Message obtainMessage()
Message obtainMessage(int what, int arg1, int arg2, Object obj)
Message obtainMessage(int what)
Message obtainMessage(int what, Object obj)
7.2.2,添加數(shù)據(jù)對象到消息隊列中
boolean sendMessage(Message msg)
boolean sendMessageAtFrontOfQueue(Message msg)
boolean sendMessageAtTime(Message msg, long uptimeMillis)
boolean sendMessageDelayed(Message msg, long delayMillis)
7.2.3屏镊,添加數(shù)據(jù)對象到消息隊列中
boolean sendEmptyMessage(int what)
boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
boolean sendEmptyMessageDelayed(int what, long delayMillis)
7.3依疼,消息的兩種處理形式
7.3.1,任務(wù)消息
handler.post(new Runnable() {
@Override
public void run() {
//TODO : Do some operation
}
});
7.3.2闸衫,數(shù)據(jù)消息
final Handler handler = new Handler() {
@Override
public void handleMessage(Message message) {
//TODO : Get the data from Message and perform opertation accordingly.
}
};
handler.sendMessage(message);
7.4涛贯,如何跟蹤消息隊列處理?
Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, TAG));
Example of tracing a message:
handler.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Executing Runnable");
}
});
mHandler.sendEmptyMessage(111);
8蔚出,Handler使用的一個簡單demo實現(xiàn)
Thread + Handler
布局代碼:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="26dp"
android:orientation="vertical"
tools:context="com.example.luolu.handlerwiththreaddemo.ThreadHandlerAndroidExample">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:autoLink="web"
android:text="http://www.baidu.com/"
android:textStyle="bold"
android:textSize="20sp"/>
<Button
android:id="@+id/start"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dp"
android:text="Start"/>
<ProgressBar
android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"
android:indeterminate="false"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="10"
android:progress="0"/>
<TextView
android:id="@+id/msg"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
ThreadHandlerAndroidExample.java:
package com.example.luolu.handlerwiththreaddemo;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
/**
-
@author luolu
*/
public class ThreadHandlerAndroidExample extends AppCompatActivity {private Handler handler = new Handler();
private MyHandlerThread myHandlerThread;Button btnStart;
ProgressBar progressBar;
TextView textMsg;@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);progressBar = (ProgressBar)findViewById(R.id.progress); textMsg = (TextView)findViewById(R.id.msg); btnStart = (Button)findViewById(R.id.start); myHandlerThread = new MyHandlerThread("myHandlerThread"); final Runnable myRunnable = new Runnable() { @Override public void run() { for (int i = 0; i <= 10; i++) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //is accessed from within inner class, needs to be declared final final int finalI = i; handler.post(new Runnable() { @Override public void run() { progressBar.setProgress(finalI); } }); } handler.post(new Runnable() { @Override public void run() { textMsg.setText("finished"); } }); } }; myHandlerThread.start(); myHandlerThread.prepareHandler(); btnStart.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myHandlerThread.postTask(myRunnable); } });
}
@Override
protected void onDestroy() {
myHandlerThread.quit();
super.onDestroy();
}public class MyHandlerThread extends HandlerThread {
private Handler handler; public MyHandlerThread(String name) { super(name); } public void postTask(Runnable task){ handler.post(task); } public void prepareHandler(){ handler = new Handler(getLooper()); }
}
}
此外弟翘,還需在清單文件中添加權(quán)限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
實現(xiàn)效果圖: