前言
在網(wǎng)上看到許多的關(guān)于Android消息機(jī)制的講解秽荤,覺的還是自己重新看書并自己寫一篇來理解會(huì)更好,消息機(jī)制的內(nèi)容也是在面試中最容易被問到的內(nèi)容课锌,因?yàn)橄C(jī)制的內(nèi)容比較多罐旗,我會(huì)分成好幾篇來進(jìn)行講解,有錯(cuò)誤也請(qǐng)大家多多包涵并指出位迂。
消息機(jī)制是什么
從一般角度來說访雪,Android的開發(fā)機(jī)制指的就是Handler,它是Android消息機(jī)制的上層接口掂林,我們只需要對(duì)他進(jìn)行交互就好了臣缀,它也是Android開發(fā)中最經(jīng)常接觸的一個(gè)消息機(jī)制,Handler默認(rèn)關(guān)聯(lián)主線程泻帮,雖然要提供Runnable參數(shù) 精置,但默認(rèn)是直接調(diào)用Runnable中的run()
方法。對(duì)于一些異步的耗時(shí)操作還有最重要的更新UI這些就都需要通過Handler來進(jìn)行實(shí)現(xiàn)锣杂,我們也就從這里開始入手脂倦。
Handler的基本使用
package com.ju.handlerdemo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
public class HandlerActivity extends AppCompatActivity {
private Button b1;
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
//這里更新業(yè)務(wù)邏輯或者更新UI
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
b1 = findViewById(R.id.button1);
b1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread() {
@Override
public void run() {
Message message = new Message();
//這個(gè)是標(biāo)志,可以發(fā)送多個(gè)元莫,比如arg1,arg2等等狼讨。
message.what = 1;
handler.sendMessage(message);
}
}.start();
}
});
}
}
這里先創(chuàng)建了一個(gè)Handler的對(duì)象,并重寫了handleMessage()
的方法柒竞,這個(gè)方法就是執(zhí)行主要的業(yè)務(wù)邏輯的政供,然后創(chuàng)建了一個(gè)子線程,在子線程里面創(chuàng)建了一個(gè)Message的對(duì)象朽基,這個(gè)Message就是Handler要處理的對(duì)象布隔,Message有多個(gè)字段。
public void testMessage(){
Message message = new Message();
//傳入的是int類型
message.what = 1;
message.arg1 = 2;
message.arg2 = 3;
//傳入的是Object類型
message.obj = null;
}
我們發(fā)現(xiàn)不僅是可以傳入int值稼虎,其實(shí)也可以傳入Object的類型衅檀,這樣Handler就可以執(zhí)行一些更復(fù)雜的操作了,然后在通過sendMessage()
這個(gè)方法來把消息發(fā)送出去霎俩,message對(duì)象就會(huì)傳到Handler的handleMessage()
里根據(jù)字段進(jìn)行具體的業(yè)務(wù)邏輯處理哀军。注意的是Handler的handleMessage()
這時(shí)候就已經(jīng)是在主線程中進(jìn)行了沉眶,而不是在子線程中進(jìn)行運(yùn)行。這就是最基本的使用方法了杉适。
Handler工作原理
這里我們就先不從源碼開始解析(因?yàn)樵创a有點(diǎn)多= =)谎倔,這里大概的講解一下Handler的工作原理,Handler主要就是由4個(gè)部分組成:Message猿推、Handler片习、MessageQueue和Looper。而Handler的運(yùn)行的底層就需要有
MessageQueue和Looper來進(jìn)行支撐蹬叭。我們一個(gè)個(gè)來看它們的作用藕咏。
Meesgae
它就是Handler之間的傳遞的消息,是一個(gè)封裝了需要傳遞給Handler數(shù)據(jù)的對(duì)象秽五,也可以攜帶Object和int類型的消息來傳遞到Handler進(jìn)行處理孽查,剛剛上面也展示過了。Handler
它就是用來發(fā)送和處理消息的坦喘,一般都是通過sendMessage()
和sendEmptyMessageAtTime()
來進(jìn)行發(fā)送消息盲再,擔(dān)任也有其他發(fā)送消息的方法,具體有什么區(qū)別我們稍后再講起宽,通過Handler進(jìn)行發(fā)送消息后洲胖,經(jīng)過一系列的處理,就會(huì)傳遞到handleMessage()
里面去來進(jìn)行處理坯沪。MessageQueue
它是一個(gè)消息隊(duì)列绿映,是一個(gè)單鏈表,通過handler發(fā)送出來的消息都會(huì)傳遞到這個(gè)消息隊(duì)列里面腐晾,然后依次的被等待處理叉弦,每一個(gè)線程只會(huì)有一個(gè)MessageQueue。-
Looper
他是一個(gè)輪詢器藻糖,通過Looper的loop()
方法淹冰,就會(huì)進(jìn)入到一個(gè)無限的循環(huán)中,然后當(dāng)MessageQueue里有一條待處理的消息巨柒,就會(huì)取出來分發(fā)到Handler的handleMessage()
方法中進(jìn)行處理樱拴,每一個(gè)線程也只會(huì)有一個(gè)Looper對(duì)象。
以上總結(jié)經(jīng)過Looper發(fā)送到handleMessage()
的時(shí)候晶乔,也就代表了從子線程轉(zhuǎn)到了主線程,這樣就可以進(jìn)行UI的處理了牺勾,這里補(bǔ)充一句正罢,關(guān)于UI更新還有一個(gè)方法就是runOnUiThread()
方法,它也是一個(gè)異步消息的接口封裝驻民,背后的原理其實(shí)跟上面描述的原理是一樣的翻具。
Message
發(fā)現(xiàn)沒有履怯,如果要進(jìn)行Handler的傳遞,就一定需要一個(gè)Message裆泳,但是如果每次需要調(diào)用的Handler的時(shí)候就創(chuàng)建一個(gè)Message實(shí)例化的話叹洲,又會(huì)非常的浪費(fèi)資源。其實(shí)創(chuàng)建Message有其它的方法晾虑。
//通過Handler取出Message
Message message1 = handler.obtainMessage();
//通過Message自帶的obtain()來取出Message
Message message2 = Message.obtain();
我們這里先看第一種的源碼吧
//Handler類
public final Message obtainMessage()
{
return Message.obtain(this);
}
//Message類
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
//也是第二種綁定方式的源碼
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
Handler類里的obtainMessage()
其實(shí)就是調(diào)用了Message的obtain()
方法疹味,然后再去調(diào)用了Message內(nèi)部類的obtain()
方法來返回Message仅叫。而第二種綁定方式就是直接調(diào)用了obtain()
就直接創(chuàng)建了Message帜篇,這樣子的效率其實(shí)是會(huì)更高的,源碼這里發(fā)現(xiàn)其實(shí)Message是一個(gè)鏈表诫咱,這里判斷如果是空鏈表的話笙隙,就創(chuàng)建一個(gè)Message,然后將頭鏈表移除掉坎缭,將頭鏈表的下一個(gè)作為新的節(jié)點(diǎn)竟痰,并維護(hù)sPoolSize,sPoolSize就是節(jié)點(diǎn)數(shù)量掏呼,而且還加了個(gè)synchronized的鎖坏快。這樣子就減少了對(duì)Message的重復(fù)利用,也不會(huì)一直創(chuàng)建Message對(duì)象憎夷,提高了效率莽鸿。
Handler的發(fā)送
Handler的發(fā)送方法有很多拾给,這里說幾個(gè)比較常見的祥得。
- sendMessage(Message msg)
這個(gè)也是基本的發(fā)送方式,傳入Message就可以發(fā)送到Handler進(jìn)行處理了蒋得。 - sendEmptyMessage(int what)
其實(shí)里面就是調(diào)用了sendEmptyMessageDelayed()
這個(gè)方法傳入要識(shí)別的信息级及,只是這個(gè)信息是int類型的。 - sendEmptyMessageDelayed(int what, long delayMillis)
這個(gè)也是一樣的额衙,在sendEmptyMessage()
里面其實(shí)就是調(diào)用了這個(gè)sendEmptyMessageDelayed()
方法饮焦,第二個(gè)參數(shù)就是代表了消息發(fā)送的延時(shí)而已。
Handler的內(nèi)存泄露
其實(shí)如果直接調(diào)用重寫Handler的handlerMessage()
是有可能會(huì)引起內(nèi)存泄露的窍侧,官方并不建議這樣使用Handler县踢,可以按照官方給出的解決辦法的使用方法。
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
return false;
}
});
參考
- Handler消息機(jī)制之深入理解Message.obtain()
- [郭霖] 第一行代碼Android(第二版)
- [任玉剛]Android開發(fā)藝術(shù)探索