知識點(diǎn)1.Handler中重要的幾個(gè)概念
主線程
:應(yīng)用程序第一次啟動,會自動開啟的一條線程及刻。主要用來處理更新UI的操作岂丘。
子線程
:程序手動開啟的一條線程。主要用來執(zhí)行一些耗時(shí)任務(wù)销部。
Message
:線程間通信的數(shù)據(jù)單元摸航。主要用于存儲子線程要向主線程傳遞的數(shù)據(jù)內(nèi)容
MessageQueue
:消息隊(duì)列,Handler存在的必要條件舅桩。主要用于存儲Messge對象酱虎,是一種單鏈表形式,采用先進(jìn)先出
Looper
:循環(huán)器擂涛,MessageQueue與Handler的通信媒介,Handler存在的必要條件读串。主要用來循環(huán)從MessageQueue取出Message,并將Message分配給對應(yīng)的Handler
知識點(diǎn)2.Handler 的流程圖
流程分析
:Handler有兩個(gè)作用,分別是發(fā)送消息和接收并處理消息恢暖。先從發(fā)送消息說·起排监,發(fā)送消息時(shí),會將Message發(fā)送到MessageQueue隊(duì)列中杰捂,Looper會不斷循環(huán)從MessageQueue取出Message發(fā)送給對應(yīng)的Handler舆床,Handler接收傳遞過來的消息,并進(jìn)項(xiàng)相應(yīng)的邏輯處理嫁佳。由于Handler要發(fā)送Message挨队,而Message需要存儲到一個(gè)隊(duì)列中才能得以保存,證明必須需要MessageQueue,而
MessageQueue并不是我們自己手動創(chuàng)建的脱拼,是Looper創(chuàng)建時(shí)候便會自動創(chuàng)建MessageQueue隊(duì)列
瞒瘸,且Handler處理消息是由Looper進(jìn)行分發(fā)
,故我們知道了MessageQueue和Looper是Handler存在必要條件熄浓。
知識點(diǎn)3.創(chuàng)建Looper的兩種方式:
(1)Looper.prepareMainLooper():在主線程
當(dāng)應(yīng)用啟動時(shí)情臭,在main()執(zhí)行。不需要我們手動調(diào)用赌蔑。這就是為什么Handler我們可以直接寫在主線程中俯在,并進(jìn)行處理UI相關(guān)邏輯。而在子線程我們就需要自己調(diào)用Looper
(1)Looper.prepare():在子線程
當(dāng)需要啟動一個(gè)Handler娃惯,需在new Handler()之前先寫Looper.prepare()跷乐,否則會報(bào):
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
Handler必須綁定線程才能使用,而它綁定了Looper便會綁定Looper所在的線程
趾浅。故必須有Looper,且Looper不能為空愕提。
知識點(diǎn)4.MessageQueue和Looper之間的關(guān)系:
MessageQueue主要任務(wù)是用來存儲Message,采用單鏈表的形式,先進(jìn)先出的方式
皿哨。
MessageQueue是什么時(shí)候創(chuàng)建的呢浅侨?
(1)進(jìn)入Looper.prepare()
public static void prepare() {
prepare(true);
}
(2)進(jìn)入prepare()
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
(3)進(jìn)入new Looper(),Looper的構(gòu)造方法
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
至此會發(fā)現(xiàn)证膨,Looper初始化便會創(chuàng)建一個(gè)對應(yīng)的MessageQueue如输。
生成Looper和MessageQueue后,便會自動進(jìn)入消息循環(huán)(Looper.loop())
央勒,當(dāng)有消息便會進(jìn)行操作不见,沒有便會掛起。
知識點(diǎn)5.消息循環(huán)(Looper.loop()):
public static void loop() {
final Looper me = myLooper();//分析點(diǎn)1
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;;//分析點(diǎn)2
...
for (;;) {//分析點(diǎn)3
Message msg = queue.next(); // might block //分析點(diǎn)4
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
...
try {
msg.target.dispatchMessage(msg);//分析點(diǎn)5
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
...
msg.recycleUnchecked();
}
}
分析Looper.loop()中的源碼崔步,是如何開啟的消息循環(huán)并把消息發(fā)給Handler?
分析點(diǎn)1
:Looper me = myLooper(),進(jìn)入 myLooper()->sThreadLocal.get(),此處Looper就是從ThreadLocal
中獲取(ThreadLocal:線程中的數(shù)據(jù)存儲類稳吮,這里用于存儲每個(gè)線程對應(yīng)的Looper
)
分析點(diǎn)2
:MessageQueue是從Looper中獲取的,每個(gè)Looper有一個(gè)對應(yīng)MessageQueue
分析點(diǎn)3
:這里是個(gè)死循環(huán)井濒,不斷從MessageQueue中獲取待處理Message
分析點(diǎn)4
:這里是個(gè)消息出隊(duì)
的操作灶似,若MessageQueue不為空慎陵,就將此消息進(jìn)行出隊(duì)。若為空就會掛起
Looper
分析點(diǎn)5
:msg.target.dispatchMessage(msg);這里msg.target通過其它源碼可知喻奥,指代的是Handler,將Message和Handler連在了一起,將消息分發(fā)給對應(yīng)的handler,完成了消息的發(fā)送
,dispatchMessage下一個(gè)知識點(diǎn)繼續(xù)分析捏悬。
消息循環(huán)的操作 = 消息出隊(duì) + 分發(fā)給對應(yīng)的Handler實(shí)例
知識點(diǎn)6.handler.dispatchMessage():
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
第一步:判斷callback 是否為空撞蚕,不為空則是handler,post()方式(下面會說handler的兩種方式),回調(diào)Runnable種重寫的 run()
第二步:為空則是handler.sendMessage(message)方式过牙,回調(diào)handleMessage()
知識點(diǎn)7.handler的兩種使用方法:
方法1
在子線程中調(diào)用甥厦,主線程中處理
new Thread(new Runnable() {
@Override
public void run() {
Message message=Message.obtain();
message.what=1;
message.obj="hello";
handler1.sendMessage(message);
}
});
Handler handler1 = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case 1:
//更新UI
break;
}
return false;
}
});
這里涉及到一個(gè)知識點(diǎn),Message的創(chuàng)建
,有兩種方式
(1) Message message=new Message();
(2) Message message=Message.obtain();
更推薦第二種方式
通過源碼查看,Message.obtain()方法中有一個(gè)線程池寇钉,如果線程池不為空刀疙,先是取出線程池中的線程,對其所有值賦值為空扫倡。屬于線程的復(fù)用
谦秧。避免了每次都new重新分配內(nèi)存空間。
方法2
在主線程中創(chuàng)建Handler,然后直接調(diào)用即可
Handler handler2 = new Handler();
handler2.post(new Runnable() {
@Override
public void run() {
//更新UI
}
});
發(fā)現(xiàn)handler.post()方式并沒有創(chuàng)建Message撵溃,它也并不是內(nèi)部啟動了一個(gè)線程疚鲤。而是將Runnable封裝成一個(gè)Message,也是傳遞到了MessageQueue缘挑,其余操作和流程便是一致了集歇。
總結(jié)
(1)handler.post()不用創(chuàng)建外部Message;handler.sendMessage()需要傳入Message
(2)兩種方法的消息處理方式不同
知識點(diǎn)8.一道面試題:主線程中Looper的輪詢死循環(huán)為何沒有阻塞主線程语淘?
參考答案:Android是依靠事件驅(qū)動的诲宇,通過Loop.loop()不斷進(jìn)行消息循環(huán),可以說Activity的生命周期都是運(yùn)行在 Looper.loop()的控制之下惶翻,一旦退出消息循環(huán)姑蓝,應(yīng)用也就退出了。而所謂的導(dǎo)致ANR多是因?yàn)槟硞€(gè)事件在主線程中處理時(shí)間太耗時(shí)维贺,因此只能說是對某個(gè)消息的處理阻塞了Looper.loop()它掂,反之則不然。
知識點(diǎn)9.新增:HandlerThread
HandlerThread:繼承Thread,封裝一個(gè)Handler
按順序依次執(zhí)行發(fā)送的Message
實(shí)現(xiàn)步驟:
1.聲明初始化HandlerThread
2.線程開始 start()
3.初始化工作線程溯泣,new Handler(這里傳參為HandlerThread中初始化的Looper)虐秋,所以此工作線程是子線程,不是主線程垃沦,不可以用來進(jìn)行更新UI客给。重寫handleMessage()處理傳遞進(jìn)來的消息
4.發(fā)送消息
5.線程關(guān)閉handlerThread.quit()
完整代碼:
public class MainActivity extends AppCompatActivity {
HandlerThread handlerThread;
Handler mainHandler;
Handler workHandler;
private Button btFirst, btSecond,btQuit;
private TextView tvMsg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btFirst = (Button) findViewById(R.id.btFirst);
btSecond = (Button) findViewById(R.id.btSecond);
btQuit= (Button) findViewById(R.id.btQuit);
tvMsg = (TextView) findViewById(R.id.tvMsg);
mainHandler = new Handler();
//1.初始化 名字自定義,用來標(biāo)識線程
handlerThread = new HandlerThread("myThread");
//2.線程開始
handlerThread.start();
//3.綁定HandlerThread的線程,是thread子線程肢簿,所以不能進(jìn)行更新ui操作
workHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(final Message msg) {
final String message = msg.obj.toString();
switch (msg.what) {
case 1:
mainHandler.post(new Runnable() {
@Override
public void run() {
tvMsg.setText(message);
}
});
break;
case 2:
mainHandler.post(new Runnable() {
@Override
public void run() {
tvMsg.setText(message);
}
});
break;
}
}
};
//4.發(fā)message
btFirst.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = Message.obtain();
message.what = 1;
message.obj = "我是第一個(gè)按鈕";
workHandler.sendMessage(message);
}
});
btSecond.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message message = Message.obtain();
message.what = 2;
message.obj = "我是第二個(gè)按鈕";
workHandler.sendMessage(message);
}
});
//5.停止
btQuit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handlerThread.quit();
//當(dāng)handler被停止后靶剑,再調(diào)用sendMessage()會報(bào):正在操作一個(gè)銷毀了的handler蜻拨,故之后不應(yīng)在操作或操作之前加判斷
}
});
}
}
分享一篇Handler好文章:http://www.reibang.com/p/b4d745c7ff7a