版權(quán)聲明:本文為博主原創(chuàng)文章渗稍,未經(jīng)博主允許不得轉(zhuǎn)載。
PS:轉(zhuǎn)載請(qǐng)注明出處
作者: TigerChain
地址: http://www.reibang.com/p/73e5fd7eb7da
本文出自 TigerChain 簡(jiǎn)書 Android 系列
教程簡(jiǎn)介
- 1团滥、閱讀對(duì)象
本篇教程適合新手閱讀竿屹,老手直接略過(guò)
-
2、教程難度
初級(jí)
正文
摘要:
Handler 在 Android 的作用主要是線程間通訊的灸姊,現(xiàn)在也有各種文章在講解 Handler 的作用以及源碼分析拱燃,但是必定這些都是別人自己的總結(jié)和整理,和自己總結(jié)還是有區(qū)別的力惯,為了加深自己的記憶所以自己也來(lái)分析一下 Handler 以及它的小伙伴們碗誉。
什么是 Handler
什么是 Handler?
再華麗的解釋也不過(guò)是官方的解解釋吧:先看官網(wǎng)上的一段話
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.
大概意思就是 Handler 可以發(fā)送 Message 和 Runnable 對(duì)象發(fā)送到 Handler 所關(guān)聯(lián)的線程隊(duì)列中去,每個(gè) Handler 的實(shí)例都會(huì)綁定到創(chuàng)建它的線程中父晶。
Handler 的作用
再來(lái)看官網(wǎng)雜說(shuō)的
There are two main uses for a Handler:
(1) to schedule messages and runnables to be executed as some point in the future
(2) to enqueue an action to be performed on a different thread than your own.
大概意思就是說(shuō)哮缺,Handler 有兩個(gè)用途:
(1)、在某一時(shí)刻執(zhí)行某些事情
(2)甲喝、在不同的線程中執(zhí)行操作 (也就是線程間通信)
Handler 常用的方法
方法名 | 描述 |
---|---|
boolean post (Runnable r) | 立即執(zhí)行操作 |
postAtTime(Runnable, long) | 在指定的時(shí)間執(zhí)行某些操作 |
postDelayed(Runnable, long) | 延時(shí)執(zhí)行某些操作 |
sendEmptyMessage(int) | 發(fā)送一個(gè)含有what的信息 |
sendMessage(Message) | 發(fā)送一個(gè)Message |
sendMessageAtTime(Message, long) | 在指定的時(shí)間發(fā)送消息 |
sendMessageDelayed(Message, long) | 延時(shí)發(fā)送消息 |
Handler 默認(rèn)是運(yùn)行在 UI 線程中的蝴蜓,默認(rèn)默認(rèn)默認(rèn)...哦 一定記得,至于為什么俺猿,我們后面會(huì)說(shuō)的茎匠。
Handler 常用的使用方式
1、解決 ANR押袍,子線程更新UI線程
在Android應(yīng)用程序中我們最常見的異步莫過(guò)于 ANR 了(主線程執(zhí)行耗時(shí)操作了,發(fā)起網(wǎng)絡(luò)請(qǐng)求诵冒,或操作 IO 等),通常情況下在 Activity 是5秒鐘響應(yīng)就會(huì)報(bào)這個(gè)錯(cuò)谊惭,在 BroadCast 中是 10 秒鐘汽馋,使用 Handler 可以很容易的解決這個(gè)問(wèn)題。
解決方法:
- 1圈盔、定義 Handler 并重寫 handleMessage 方法
//在UI線程中
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
//取得傳遞過(guò)來(lái)的 msg
String passedChangeTv = (String) msg.obj;
tv.setText(passedChangeTv);
break ;
}
}
} ;
- 2豹芯、在子線程中去執(zhí)行耗時(shí)操作
//比如請(qǐng)求服務(wù)器,然后更新 TextView
new Thread(new Runnable() {
@Override
public void run() {
//執(zhí)行耗時(shí)操作
/**
* 1 請(qǐng)求服務(wù)器
* 2 解析數(shù)據(jù)
* 3 獲取數(shù)據(jù)
*/
//比如從服務(wù)器取得的數(shù)據(jù)是" Handler 應(yīng)用"
String changeTv = "Handler應(yīng)用" ;
Message message = new Message() ;
message.what = 1 ;
message.obj = changeTv ;
mHandler.sendMessage(message) ;
}
}).start();
這樣就可以解決 ANR
2、可以用作輪詢驱敲,或是定時(shí)器
- 1铁蹈、舉個(gè)栗子,我們要一秒鐘打印一個(gè)字符串,我們可以使用 Handler 的 postDelayed 方法
private Handler mtimeTaskHandler = new Handler() ;
private void timerTaskHandler() {
//調(diào)用Handler自帶的
mtimeTaskHandler.postDelayed(new Runnable() {
@Override
public void run() {
Log.e("===","TAG:"+ System.currentTimeMillis()/1000) ;
mtimeTaskHandler.postDelayed(this,1000) ;
}
},1000) ;
}
- 2众眨、一秒鐘請(qǐng)求一次服務(wù)器握牧,然后把服務(wù)器的最新數(shù)據(jù)顯示在 TextView 容诬,在這里我們使用 Handler 的sendMessageDelayed(Message msg,Long deayTime)方法
private Handler preSecondHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
//取得傳遞過(guò)來(lái)的 msg
int passedChangeTv = (int) msg.obj;
tv.setText(passedChangeTv+"");
//再次調(diào)用獲取服務(wù)器最新數(shù)據(jù)方法
preSecondGetServer() ;
break ;
}
}
} ;
private int i ;
private void preSecondGetServer() {
new Thread(new Runnable() {
@Override
public void run() {
//假設(shè)這里從服務(wù)器取回來(lái)的 i
i++ ;
Message message = new Message() ;
message.what = 1 ;
message.obj = i ;
preSecondHandler.sendMessageDelayed(message,1000) ;
}
}).start();
這樣就可以一秒請(qǐng)求一次服務(wù)器,并且把結(jié)果顯示在 TextView 上了
3沿腰、兩個(gè)子線程進(jìn)行通信
Handler的作用就是是進(jìn)行線程間通信的览徒,上面說(shuō)的都是UI線程(主線程)和子線程之間的通信,那么兩個(gè)工作線程(子線程)可以通信嗎颂龙,答案是肯定 的习蓬。
- 1、先聲明 Handler 所在的線程
//成員變量 子線程中的 Handler
private Handler threadHandler ;
class MyThread extends Thread{
{
//一實(shí)例化就開啟線程 代碼塊
start();
}
@Override
public void run() {
super.run(); // Handler 所在的線程默認(rèn)是沒(méi)有 looper 的(除了 Activity )
Looper.prepare();
threadHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//取得傳遞過(guò)來(lái)的 msg
String passedChangeTv = (String) msg.obj;
//設(shè)置顯示
tv.setText(passedChangeTv);
}
} ;
Looper.loop();
}
}
注意:子線程默認(rèn)是沒(méi)有 Looper 的所以我們?cè)谧泳€程中一定要聲明 Looper.prepare() 和 Looper.loop() 在它們之間去初始化 Handler
- 2措嵌、在另一個(gè)子線程中發(fā)送消息
private void threadHandlerTask() {
new Thread(new Runnable() {
@Override
public void run() {
String changeTv = "Handler應(yīng)用" ;
Message message = new Message() ;
message.what = 1 ;
message.obj = changeTv ;
threadHandler.sendMessage(message) ;
}
}).start() ;
}
- 3友雳、最后在合適的地方調(diào)用,在這里我們?yōu)榱搜菔揪驮?Activity 的onCreate() 方法中調(diào)用
MyThread t = new MyThread() ;
threadHandlerTask() ;
以上就完成了兩個(gè)子線程通過(guò) Handler 來(lái)通信。
注意:上面的子線程能信有問(wèn)題铅匹,不知道細(xì)心的朋友發(fā)現(xiàn)了沒(méi)有押赊,我故意留了一個(gè) bug,忽略此 bug 不提包斑,我們上面的程序性能是有問(wèn)題的流礁,我們一一解決。
1罗丰、上面代碼存在的 bug
細(xì)心的朋友會(huì)說(shuō)我靠神帅,擦,F(xiàn)K,竟然在子線程中更新 UI萌抵,我們要以清楚的看到 threadHandler 是在子線程中的找御,那么我們是不能更新 UI的。會(huì) ANR 的好不好呀绍填,沒(méi)錯(cuò)就是這個(gè)問(wèn)題霎桅,不怕,程序員就是發(fā)現(xiàn)問(wèn)題并解決問(wèn)題的讨永,我們來(lái)解決這個(gè)問(wèn)題滔驶,修改代碼如下。
- 1卿闹、首先在 MainActivity 中聲明一個(gè) threadHander( threadHander 一定是在主線程中的)
private Handler threadHander = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//取得傳遞過(guò)來(lái)的 msg
String passedChangeTv = (String) msg.obj;
tv.setText(passedChangeTv+"");
}
} ;
所以上面代碼是可以更新 UI 的沒(méi)有問(wèn)題
- 2揭糕、聲明子線程
class MyThread extends Thread{
private Looper mLooper ;
private Handler mHandler ;
/**
* 代碼塊或是構(gòu)造方法中開啟線程都可以
*/
public MyThread(){
Log.e("t所在的線程",this.getName()) ;
start();
}
// {
// //一實(shí)例化就開啟線程 代碼塊
// start();
// }
@Override
public void run() {
super.run(); // Handler 所在的線程默認(rèn)是沒(méi)有 looper 的(除了Activity)
Looper.prepare();
mLooper = Looper.myLooper() ;
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//取得傳遞過(guò)來(lái)的 msg
String passedChangeTv = (String) msg.obj;
//設(shè)置顯示 這里的 Handler 運(yùn)行在子線程中去的,所以不能更新UI
// tv.setText(passedChangeTv);
threadHander.sendMessage(threadHander.obtainMessage(1,passedChangeTv)) ;
}
} ;
Looper.loop();
}
public Handler getThreadHandler(){
return mHandler!=null?mHandler:null ;
}
}
- 3、在另一個(gè)線程中去發(fā)送消息
private void threadHandlerTask() {
new Thread(new Runnable() {
@Override
public void run() {
String changeTv = "Handler應(yīng)用" ;
Message message = new Message() ;
message.what = 1 ;
message.obj = changeTv ;
t.getThreadHandler().sendMessage(message) ;
}
}).start() ;
}
此處的t就是 MyThread 的實(shí)例锻霎,聲明成成員變量著角,不用糾結(jié)
我們來(lái)捋一捋流程,首先調(diào)用
t.getThreadHandler().sendMessage(message)
將消息發(fā)送到 MyThread 的 Handler 中旋恼,然后在 MyThread 的 Handler (子線程中)中將消息發(fā)送到 MainActivity 的 threadHander(UI線程中)吏口,這樣就完成了,子線程更新UI線程的過(guò)程
注:這里是為了顯示更新UI線程所以多次發(fā)送,如果只是為了兩個(gè)線程間通信锨侯,那么 threadHander 是可以不需要的
我們把改后的代碼運(yùn)行一下,并且我添加了一些線程的日志冬殃,看效果
從圖中我們可以看到囚痴,子線程間通信是完全沒(méi)有問(wèn)題的,但是我們退出應(yīng)用再進(jìn)入應(yīng)用的時(shí)候發(fā)現(xiàn)审葬,線程數(shù)量在不停的增加深滚,我靠如果打開 100 次豈不是又多了 100 個(gè)線程,1000... 次呢涣觉,想都不敢想痴荐。如何解決呢,往下看官册。
2生兆、手動(dòng)調(diào)用 Looper 需要手動(dòng)釋放
接著上面繼續(xù)說(shuō),問(wèn)題出現(xiàn)在那里呢膝宁,就是手動(dòng)調(diào)用 Looper.perpare() 和 Looper.loop() 的時(shí)候一定要記得在任務(wù)完成以后鸦难,或是合適的地方要釋放掉 looper,要調(diào)用 looper.quit()员淫,結(jié)束消息隊(duì)列合蔽,進(jìn)而結(jié)束線程。如果不這么做介返,thread 會(huì)長(zhǎng)時(shí)間存在不銷毀這拴事。這就是上面所說(shuō)的性能問(wèn)題,不斷的開辟線程圣蝎,線程的開辟是需要開銷的刃宵,所以我們來(lái)解決這一問(wèn)題
- 1、我們改造MyThread類
class MyThread extends Thread{
private Looper mLooper ;
private Handler mHandler ;
/**
* 代碼塊或是構(gòu)造方法中開啟線程都可以
*/
public MyThread(){
Log.e("t所在的線程",this.getName()) ;
start();
}
// {
// //一實(shí)例化就開啟線程 代碼塊
// start();
// }
@Override
public void run() {
super.run(); //Handler所在的線程默認(rèn)是沒(méi)有l(wèi)ooper的(除了Activity)
Looper.prepare();
mLooper = Looper.myLooper() ;
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//取得傳遞過(guò)來(lái)的 msg
String passedChangeTv = (String) msg.obj;
//設(shè)置顯示 這里的Handler運(yùn)行在子線程中去的,所以不能更新UI
// tv.setText(passedChangeTv);
threadHander.sendMessage(threadHander.obtainMessage(1,passedChangeTv)) ;
}
} ;
Looper.loop();
}
//這里是新添加的方法
public void exit(){
if(mLooper!=null){
mLooper.quit();
mLooper = null ;
}
}
public Handler getThreadHandler(){
return mHandler!=null?mHandler:null ;
}
}
我們只是在 MyThread 類中添加了一個(gè) exit 方法徘公,其它的都沒(méi)有變.
- 2组去、為了方便測(cè)試,我們?cè)?onDestory() 方法中調(diào)用
@Override
protected void onDestroy() {
super.onDestroy();
Log.e("onDestroy",t.getName()+"銷毀") ;
t.exit();
t=null ;
}
我們重新跑一下應(yīng)用程序步淹,來(lái)觀察結(jié)果
從效果圖中我們可以看出从隆,當(dāng)我們手動(dòng)調(diào)用 Looper.prepare() 和 Looper.loop() 的時(shí)候創(chuàng)建一個(gè)線程,但是們退出應(yīng)用的時(shí)候會(huì)退出 Looper缭裆,線程會(huì)銷毀键闺,我們需要你的時(shí)候讓你存在,不需要的時(shí)候讓你銷毀澈驼,這就大大提高了性能辛燥。我們看始終激活的線程數(shù)量都是5,這是我們所期望的
總結(jié):在子線程中,如果手動(dòng)為其創(chuàng)建了 Looper挎塌,那么在所有的事情完成后應(yīng)該調(diào)用 quit 方法來(lái)終止消息循環(huán)
Handler 和它兄弟們
Handler 不能單獨(dú)工作徘六,必須和它的兄弟們一起協(xié)同才可以工作。而它的兄弟們就是以下三個(gè)
1榴都、Looper
Looper 字面意思是一個(gè)輪詢器待锈,是用來(lái)檢測(cè) MessageQueue 中是否有消息,如果有消息則把消息分發(fā)出去嘴高,沒(méi)有消息就阻塞在那里直到有消息過(guò)來(lái)為止竿音。
一個(gè)線程對(duì)應(yīng)一個(gè) Looper,一個(gè) Looper 對(duì)應(yīng)一個(gè) MessageQueue.
注意:默認(rèn)情況下,線程是沒(méi)有 Looper 的拴驮,所以要調(diào)用 Looper.prepare() 來(lái)給線程創(chuàng)建消息隊(duì)列春瞬,然后再通過(guò),Looper.loop() 來(lái)不停(死循環(huán))的處理消息消息隊(duì)列的消息
2套啤、MessageQueue
是用來(lái)存放消息的宽气,通過(guò) Looper.prepare() 來(lái)初始化消息隊(duì)列。MessageQueue 從字面意思來(lái)看是一個(gè)消息隊(duì)列潜沦,但是內(nèi)部實(shí)現(xiàn)并不是基于隊(duì)列的抹竹。是基于一個(gè)單鏈表的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)消息列表的,鏈表插入和刪除優(yōu)勢(shì)很明顯止潮。
3窃判、Message
傳遞信息的載體,可以攜帶一些信息喇闸,類似于 Intent袄琳,Bundle 一樣。兩個(gè)線程通過(guò) Message 聯(lián)系在一起燃乍。
Handler 源碼分析
重點(diǎn)來(lái)了唆樊,下面我們一起來(lái)分析一下Handler的源碼
1、sendMessage() 發(fā)生了什么
當(dāng)我們調(diào)用 Handler 的 sendMessage 方法的時(shí)候到底發(fā)生了什么刻蟹,我們從源碼去看一下
public final boolean sendMessage(Message msg) {
return sendMessageDelayed(msg, 0);
}
我們可以看到調(diào)用sendMessage方法調(diào)用sendMessageDelayed(msg, 0)
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
而 sendMessageDelayed 方法又調(diào)用 return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis) 方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//取得MessageQueue
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//把消息存放到MessageQueue中
return enqueueMessage(queue, msg, uptimeMillis);
}
在這里不是不有些小激動(dòng)呢逗旁,我們看到 sendMessage 最終調(diào)用的是 enqueueMessage(queue, msg, uptimeMillis) 這個(gè)方法,我們來(lái)看看這個(gè)方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
我勒個(gè)插舆瘪,還在調(diào)用片效,這里代碼我們基本上也能看懂,就是不知道m(xù)sg.target 是個(gè)毛線英古,不過(guò)他是 Message 的一個(gè)屬性淀衣,我們進(jìn)去看一下發(fā)現(xiàn) msg.target 是一個(gè)Handler,好我們?cè)倏磓ueue.enqueueMessage(msg, uptimeMillis) 方法
boolean enqueueMessage(Message msg, long when) {
//msg.target 這么熟悉召调,我去不就是 Handler 嗎膨桥,要發(fā)送一個(gè)消息肯定要有 Handler 呀蛮浑,不然發(fā)個(gè)毛毛呢
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//消息是否正在使用
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//消息插入鏈表頭,條件是如果MessageQueue為空只嚣,或是消息發(fā)送的時(shí)刻為0或者消息發(fā)送的時(shí)刻小于鏈表頭的派發(fā)時(shí)刻沮稚,就把消息插入鏈表頭
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//否則就找個(gè)合適的位置把消息插到鏈表中
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
總之這個(gè)方法就是把消息插入到消息隊(duì)列中,我們現(xiàn)在可以歸納了,當(dāng)調(diào)用handler.sendMessage方法的時(shí)候做了一件重要的事:
把消息插入到MessageQueue中
2册舞、Looper 該上場(chǎng)了
根據(jù)前面所述蕴掏,我們知道 Looper 的作用是創(chuàng)建消息隊(duì)列,和處理消息环础。
- 1囚似、Looper.prepare() 方法就是用來(lái)創(chuàng)建消息隊(duì)列的剩拢,我們看源碼
public static void prepare() {
prepare(true);
}
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));
}
我們看到 prepare 方法調(diào)用 prepare(true) 方法线得,此方法中我們目前也沒(méi)有看不懂的語(yǔ)句,無(wú)非就是 sThreadLocal 是什么東東徐伐,不要緊贯钩,我們暫且當(dāng)它是一個(gè) List 一樣用來(lái)存取 Looper 的,接下來(lái)我們看 new Looper(quitAllowed) 方法
private Looper(boolean quitAllowed) {
//初始化消息隊(duì)列
mQueue = new MessageQueue(quitAllowed);
//取得當(dāng)線線程
mThread = Thread.currentThread();
}
看到了吧办素,確實(shí)在上面方法中初始化了 MessageQueue角雷。所以我們這里總結(jié) Looper.prepare() 方法就是創(chuàng)建了一個(gè)消息隊(duì)列
根據(jù)上面的分析,我們有了消息隊(duì)列性穿,也知道 sendMessage 是把消息插入到消息隊(duì)列中去了勺三,那么如何管理 MessageQueue中 的消息呢,Looper.loop() 該上場(chǎng)了需曾。
- 2吗坚、Looper.loop(): 我們直接看源碼
public static void loop() {
//取得Looper
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
//拿到 MessageQueue
final MessageQueue queue = me.mQueue;
...省略部分代碼
for (;;) { //這里是一個(gè)死循環(huán),取出每個(gè)消息呆万,然后分發(fā)
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
... 省略部分代碼
//msg.target(Handler)這里調(diào)用 Handler 的dispatchMessage方法
msg.target.dispatchMessage(msg);
}
}
綜上代碼商源,我們可以得出結(jié)論 Lopper.loop() 方法主要作用是取出 MessageQueue 中消息然后調(diào)用 Handler 的 dispatchMessage 方法把消息分發(fā)出去(前提是 Message 中要有消息)
接下來(lái)我們看 Handler 的 dispatchMessage 方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
//如果msg.callback不為空,則調(diào)用handleCallback
handleCallback(msg);
} else { 否則谋减,mCallback不為空
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//調(diào)用handleMessage
handleMessage(msg);
}
}
似乎看到熟悉的方法了牡彻,對(duì)就是 handleMessage(msg),就是 Handler 的回調(diào)方法出爹,就是處理消息的方法庄吼。
這樣我們大體了解到,Handler的處理流程了严就,大致如下:
Looper.parpare() ; //先準(zhǔn)備好消息隊(duì)列
Handler handler = new Handler(){
@Override
handleMessage(Message msg){
//如理發(fā)送過(guò)來(lái)的消息
...
...
}
}
Looper.loop() ; //是一個(gè)死循環(huán)霸褒,一直監(jiān)聽著 MessageQueue ,從消息隊(duì)列中取消息,然后調(diào)用 handler. dispatchMessage 方法盈蛮,最后調(diào)用的就是 handler 的 handleMessage(Message msg) 方法來(lái)處理消息
那么如何把消息插入到消息隊(duì)列呢废菱,就靠 Handler 的 sendMessage() 方法了技矮,所以當(dāng)調(diào)用了 Handler 的 sendMessage(),Looper.loop() 監(jiān)聽到 MessageQueue 中有消息殊轴,就調(diào)用 dispatchMessage 方法分發(fā)消息衰倦,最終調(diào)用 handler 的 handleMessage(Message msg) 方法來(lái)處理消息。
Handler的原理如下圖所示
原創(chuàng)圖:轉(zhuǎn)載注明出處
Activity 默認(rèn)有 Looper
Activity 中的 Looper 從何來(lái)
我們知道使用 Handler 的時(shí)候旁理,Handler 一定要存在于有Looper.parpare() 和 Looper.loop() 的線程中可是我們都知道 Activity 中使用 Handler 的時(shí)候不用寫 Looper.parpare() 和 Looper.loop()樊零,但是為什么不用寫有沒(méi)有想過(guò),那么我們來(lái)分析一下
Activity 所在線程也叫 UI 線程孽文,為什么叫 UI 線程呢驻襟,其實(shí)它是通過(guò) ActivityThread 來(lái)管理的,我們知道 Android 應(yīng)用層是用 Java 開發(fā)的芋哭,那么 Java 肯定是有一個(gè)入口方法即 main 方法沉衣,main 方法到底在那里呢?
答案就是在 ActivityThread.java 中减牺,我們來(lái)看一下源碼
public final class ActivityThread {
... 省略一大坨代碼
final Looper mLooper = Looper.myLooper();
//H就是一個(gè)Handler
final H mH = new H();
... 省略若干代碼
private class H extends Handler {
省略一大堆
String codeToString(int code) {
if (DEBUG_MESSAGES) {
switch (code) {
case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY";
case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY";
case PAUSE_ACTIVITY_FINISHING: return "PAUSE_ACTIVITY_FINISHING";
case STOP_ACTIVITY_SHOW: return "STOP_ACTIVITY_SHOW";
...省略大量代碼
}
}
return Integer.toString(code);
}
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
case RELAUNCH_ACTIVITY:
...
case PAUSE_ACTIVITY:
...
break;
... 省略部分代碼
case RESUME_ACTIVITY:
...
break;
case SEND_RESULT:
...
break;
case DESTROY_ACTIVITY:
...
break;
... 省略部分代碼
case CREATE_SERVICE:
...
break;
case BIND_SERVICE:
...
break;
case UNBIND_SERVICE:
...
break;
... 省略又臭又長(zhǎng)的代碼
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}
}
public static void main(String[] args) {
...省略部分代碼
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
...省略部分代碼
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
}
從上面代碼中可以隱約的看到 Activity 的 oncreate 豌习,onresume... Service 的 oncreate bindService 等等方法都在這里處理了,再?gòu)纳厦婵吹搅?main 方法并且調(diào)用了 Looper.prepareMainLooper(); 和 Looper.loop(); 由此就知道了拔疚,為什么我們的 Activity 默認(rèn)是有 Looper 的肥隆。
結(jié)論
Activity 叫 UI 線程,其實(shí)指的就是 ActivityThread稚失,Activity 是由ActivityThread 管理啟動(dòng)栋艳,暫停等,并且 ActivityThread 的入口方法中開啟了 Looper.prepareMainLooper() 和 Looper.loop()句各,所以我們的 Activity 默認(rèn)就會(huì)有一個(gè) Looper吸占,Service 同理
Handler引起的內(nèi)存泄露
引子
我們先來(lái)看一個(gè)例子
public class MainActivity extends AppCompatActivity {
private TextView tv ;
//在UI線程中
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
...
...
}
} ;
}
乍一看,好像沒(méi)有什么問(wèn)題诫钓,但是我告訴你旬昭,這段代碼可能會(huì)存在內(nèi)存泄露的,這是為什么呢菌湃?答案是非靜態(tài)內(nèi)存部會(huì)持有外部類的引用(Java的特性)
问拘,在這里Handler是一個(gè)非靜態(tài)的內(nèi)存部類,會(huì)隱式的持有 MainActivity 的引用的惧所。
解決辦法
解決這個(gè)問(wèn)題的辦法就是避免使用非靜態(tài)內(nèi)部類骤坐。
static class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
}
}
如果要操作Activity的一些對(duì)象,這里使用弱引用
static class MyHandler extends Handler {
// WeakReference to the outer class's instance.
private WeakReference<MyActivity> mOuter;
public MyHandler(MyActivity activity) {
mOuter = new WeakReference<MyActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MyActivity outer = mOuter.get();
if (outer != null) {
// Do something with outer as your wish.
}
}
}
完整的代碼
public class MyActivity extends AppCompatActivity {
private MyHandler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new MyHandler(this);
}
@Override
protected void onDestroy() {
// Remove all Runnable and Message.
mHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
static class MyHandler extends Handler {
// WeakReference to the outer class's instance.
private WeakReference<MyActivity> mOuter;
public MyHandler(MyActivity activity) {
mOuter = new WeakReference<MyActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MyActivity outer = mOuter.get();
if (outer != null) {
// Do something with outer as your wish.
}
}
}
}
當(dāng)然我們可以把Handler單獨(dú)寫到一個(gè)類中下愈,配置 RxBus 或 EventBus來(lái)更新UI或執(zhí)行某些操作纽绍,這里就介紹完了 Handler 的相關(guān)知識(shí)點(diǎn)了
想成為一個(gè)牛 B 的人都會(huì)點(diǎn)喜歡或分享的