轉(zhuǎn)載請(qǐng)注明文章出處LooperJing!
1、UI不能在子線程中更新是個(gè)偽命題
我們常說(shuō)UI需要在主線程中進(jìn)行更新,子線程就不能更新UI嗎?不是,我們并不是說(shuō)不能在子線程中更新UI,而是說(shuō)UI必須要在它的創(chuàng)建線程中進(jìn)行更新,比如下面一段代碼在子線程更新UI就不會(huì)報(bào)錯(cuò)。
new Thread(new Runnable() {
@Override
public void run() {
TextView textView=new TextView(MainActivity.this);
textView.setText("Hello");
}
}).start();
OK,那我偏偏不在創(chuàng)建線程中進(jìn)行更新,如下!view在主線程中創(chuàng)建,在子線程中add一個(gè)Button。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final LinearLayout view = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.activity_main, null, false);
setContentView(view);
new Thread(new Runnable() {
@Override
public void run() {
Button btn=new Button(MainActivity.this);
btn.setText("Hello");
view.addView(btn);
}
}).start();
}
運(yùn)行后,Button被順利的加到了根布局中。尼瑪,“UI必須要在它的創(chuàng)建線程中進(jìn)行更新”這種說(shuō)法也不對(duì)啊!!!不是不對(duì),而是欠妥,我修改一下代碼!
new Thread(new Runnable() {
@Override
public void run() {
Button btn=new Button(MainActivity.this);
btn.setText("Hello");
SystemClock.sleep(2000);
view.addView(btn);
}
}).start();
就是在addView之前,睡眠了一會(huì),結(jié)果還是拋出了 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.這個(gè)錯(cuò)誤!通常這種錯(cuò)誤,就要用Handler向UI線程發(fā)送消息來(lái)更新!這個(gè)后面會(huì)對(duì)源碼進(jìn)行分析。說(shuō)了半天,進(jìn)入正題,我今天要討論的是Message。要掌握Handler,先對(duì)Handler發(fā)送的Message有所了解。
2、Message的初步認(rèn)識(shí)
Message就是一個(gè)實(shí)現(xiàn)了Parcelable的java類(lèi),我的第一感覺(jué)是它可以可以用在進(jìn)程間通信(IPC),可以用在網(wǎng)絡(luò)中傳輸,看一下它比較重要的幾個(gè)字段。先來(lái)幾個(gè)熟悉的。
//當(dāng)有多個(gè)Handler發(fā)消息的時(shí)候,或者一個(gè)Handler發(fā)多個(gè)消息的時(shí)候,可以用what標(biāo)識(shí)一個(gè)唯一的消息
public int what;
//當(dāng)傳遞整形數(shù)據(jù)的時(shí)候,不需要使用setData(Bundle)去設(shè)置數(shù)據(jù),使用arg1,arg2開(kāi)銷(xiāo)更小。
public int arg1;
public int arg2;
//Message所攜帶的數(shù)據(jù)對(duì)象
public Object obj;
這幾個(gè)都是很常用的,要深入了解Message,不得不了解下面幾個(gè)。
//flags表示這個(gè)Message有沒(méi)有在使用,1表示在池中,等待復(fù)用,0表示正在被使用,
int flags;
//Message發(fā)送之后,何時(shí)才能被處理
long when;
// Message所攜帶的數(shù)據(jù)
Bundle data;
//表示這個(gè)消息被哪個(gè)Handler處理
Handler target;
//我們用Handler.post一個(gè)Runnable,這個(gè)Runnable也是被包裝成Message對(duì)象的
Runnable callback;
// 作為消息鏈表所用的一個(gè)成員
Message next;
//sPoolSync是對(duì)象鎖,因?yàn)镸essage.obtain方法會(huì)在任意線程調(diào)用
private static final Object sPoolSync = new Object();
//sPool代表接下來(lái)要被重用的Message對(duì)象
private static Message sPool;
//sPoolSize表示有多少個(gè)可以被重用的對(duì)象
private static int sPoolSize = 0;
//MAX_POOL_SIZE是pool的上限,這里hardcode是50
private static final int MAX_POOL_SIZE = 50;
對(duì)于上面列舉的,挑幾個(gè)重點(diǎn)分析一下。
Runable是怎么被封裝到Message中的?我們常為會(huì)延遲做某個(gè)操作寫(xiě)出下面的代碼
handler.postDelayed(new Runnable() {
@Override
public void run() {
}
},1000);
跟蹤進(jìn)去發(fā)現(xiàn)在postDelayed中調(diào)用了getPostMessage
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
所以這個(gè)run方法只是一個(gè)普通的回調(diào)而已,千萬(wàn)不要認(rèn)為他是在另外一個(gè)線程中。剛說(shuō)了Message中的target表示了這個(gè)消息要被哪一個(gè)Handler所處理,對(duì)于Message是怎么被處理的呢?盡管后面要分析Handler源碼,這里還是提一下。
Message的處理順序
Looper.loop()方法中Message被從MessageQueue取出來(lái)后會(huì)調(diào)用msg.target.dispatchMessage(msg)
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在dispatchMessage方法中,檢查是否有由Runnable封裝的消息,如果有,首先處理;其次處理的是mCallback,mCallback是什么?mCallback是Callback的對(duì)象,Callback是Handler中的一個(gè)內(nèi)部接口,這個(gè)接口的作用是,當(dāng)你實(shí)例化一個(gè)Handler對(duì)象的時(shí)候,可以避免不去實(shí)現(xiàn)Handler的handleMessage方法。
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
從消息的分發(fā)可以看到,如果返回了true,那么handler中的handleMessge方法是不會(huì)被執(zhí)行的,如果返回false或者mCallback沒(méi)有被賦值,那么就會(huì)回調(diào)Handlerd的handleMessge,這就是一個(gè)Message的分發(fā)流程。了解了Message的分發(fā)之后,那么Message是怎么被創(chuàng)建的呢。
3、Message全局池
ANDROID系統(tǒng)中很多操作都是靠發(fā)消息完成的,比如按下返回鍵就是一個(gè)消息,這樣系統(tǒng)會(huì)不會(huì)構(gòu)建大量的Message對(duì)象呢?上面看到getPostMessage方法中,以 Message.obtain()的方式獲取一個(gè)消息,而不是通過(guò)它的構(gòu)造函數(shù),獲取消息的代碼很簡(jiǎn)單。
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
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();
}
有一個(gè)關(guān)鍵詞是sPool,初步感覺(jué)是池的意思,別忘記了,在第一節(jié)Message的初步認(rèn)識(shí)中,sPool是一個(gè)Message對(duì)象 ,這里有點(diǎn)蒙逼,一個(gè)對(duì)象起個(gè)名字跟池有什么關(guān)系。回顧一下,Message還有一個(gè)成員是 next。
// sometimes we store linked lists of these things
Message next;
這樣每一個(gè)Message對(duì)象都有一個(gè)next指針指向下一個(gè)可用的Message,這不就是大學(xué)數(shù)據(jù)結(jié)構(gòu)中的鏈表嘛!所以一個(gè)消息池的結(jié)構(gòu)是這樣的。
既然知道Message有一個(gè)消息池(我們通常稱(chēng)這個(gè)消息池為全局池)的機(jī)制,那么它設(shè)計(jì)初衷肯定是要做到復(fù)用的,那么還有一個(gè)問(wèn)題,Message何時(shí)被入池,何時(shí)出池?(MessageQueue雖然是存儲(chǔ)消息的,但要弄清楚,這里說(shuō)的消息池跟MessageQueue并沒(méi)有什么關(guān)系。)
消息要入池,也就是這個(gè)消息被回收到池中,等待復(fù)用,所以我們大膽猜測(cè)這個(gè)消息肯定不在使用之中,如果這個(gè)消息正在使用之中,是肯定不會(huì)把它放到全局池里面的,也就是說(shuō)只有這個(gè)消息完成了它的使命,系統(tǒng)才能把它回收到全局池中。通過(guò)這樣的分析,消息入池不是在它的創(chuàng)建階段,而是在回收階段。直接看代碼吧。Message中有一個(gè)recycle的方法。
/**
* Return a Message instance to the global pool.
* <p>
* You MUST NOT touch the Message after calling this function because it has
* effectively been freed. It is an error to recycle a message that is currently
* enqueued or that is in the process of being delivered to a Handler.
* </p>
*/
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
首先判斷消息是否在使用之中
boolean isInUse() {
return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
如果在使用之中,繼續(xù)判斷gCheckRecycle,gCheckRecycle的默認(rèn)值是true,這個(gè)一個(gè)與版本相關(guān)的常量,在5.0之后的版本這個(gè)值是false,不知道作什么使用,有誰(shuí)知道可以告訴我一下。在此不糾結(jié)了。如果不在使用之中,最后會(huì)走進(jìn)recycleUnchecked。
/**
* Recycles a Message that may be in-use.
* Used internally by the MessageQueue and Looper when disposing of queued Messages.
*/
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
這段代碼也不是很長(zhǎng),很好理解,一開(kāi)始,把這個(gè)消息所有成員賦值成最初的狀態(tài),F(xiàn)LAG_IN_USE的值是1一開(kāi)始說(shuō)了Message的flags表示這個(gè)Message有沒(méi)有在使用,1表示在池中,等待復(fù)用,0表示正在被使用。重點(diǎn)看同步鎖中的代碼。
假設(shè)全局池沒(méi)有元素時(shí),我們將第一個(gè)消息放到池中,sPool一開(kāi)始是NULL,next指向了sPool,所以此時(shí)的消息的sPool和next都是NULL,然后sPool指向當(dāng)前的Message對(duì)象,最后池的數(shù)量加1。大致如下圖。
假設(shè)有來(lái)個(gè)消息m2,在走一遍同步鎖中的代碼,此時(shí)全局池的狀態(tài)如下圖所示。
同理,三個(gè)消息的時(shí)候,是這樣
看到這,相信你已經(jīng)知道了消息是怎么加到全局池中的,那么何時(shí)出池呢?再看消息的獲取代碼,嘗試找出消息何時(shí)出池的。
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
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();
}
obtain經(jīng)常使用在多線程之中,所以用sPoolSync作為同步鎖,第一次sPool為空,會(huì)new一個(gè)消息返回,這new的消息會(huì)在回收的時(shí)候,會(huì)被加到全局池中。如果sPool不為空,sPool是什么?sPool是指向全局池的頭指針,sPool不為空,說(shuō)明了全局池中有元素。把sPool賦值給一個(gè)Message對(duì)象m,同時(shí)全局池的頭指針向后移,指向下一個(gè)被復(fù)用的消息,然后把m的flags賦值為0,表示這個(gè)消息被復(fù)用了,池中元素?cái)?shù)量減1。經(jīng)過(guò)這個(gè)邏輯,上圖中的消息m3是不是就被出池了呢?
OK,本文分析到這里,一個(gè)Message大部分內(nèi)容都被分析了,我們知道了Message內(nèi)部有一個(gè)全局池,保證了開(kāi)發(fā)者可以不構(gòu)建大量的消息,提高性能。我們也知道了一個(gè)消息何時(shí)入池,何時(shí)出池。OK,Message復(fù)用機(jī)制到此結(jié)束。
Please accept mybest wishes for your happiness andsuccess