在上一篇的文章中摆寄,我們提到了在子線程中是無法對UI界面進(jìn)行更新的。那么為啥android中無法在子線程中隊UI進(jìn)行更新呢踩窖?既然這樣又如何才能在子線程中對UI進(jìn)行更新呢翘县?這就是我們這篇文章所要聊聊的。
其實所謂的在子線程中無法對UI進(jìn)行更新市袖,這句話是不嚴(yán)謹(jǐn)?shù)姆戎保鋵嵕€程能否更新UI的關(guān)鍵在于viewroot是否在該線程烁涌。
每當(dāng)我們打開一個android app時,android就會開辟一個對應(yīng)的主線程酒觅,而這個主線程就是這個app的UI線程撮执,即ActivityThread。UI線程主要負(fù)責(zé)與UI相關(guān)的事件舷丹。android系統(tǒng)并不會為每個組件去單獨創(chuàng)建一個線程抒钱。所以android UI操作不是線程安全的,所有的操作都必須在UI線程中執(zhí)行颜凯。即android使用的是單線程模型谋币。
那么android就為我們提供了一個很好用的工具Handler,它允許我們在子線程中去對UI進(jìn)行操作症概。那么Handler究竟該如何使用呢瑞信?
閑話少說,直接提問穴豫,如何做到每2秒去更新UI界面中的一個TextView凡简。
在java中,這可以說很容易實現(xiàn)精肃,直接調(diào)用TimerTask即可秤涩。但是在android中這樣去寫,我們可以發(fā)現(xiàn)并不能達(dá)到我們預(yù)期的效果司抱。那么現(xiàn)在使用Handler來試試看筐眷。
```
public class MainActivity extends Activity {
private int i = 0;
private TextView tv;
private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
update();
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
Timer timer = new Timer();
timer.scheduleAtFixedRate(new MyTask(), 1, 1000*2);
}
private class MyTask extends TimerTask{
@Override
public void run() {
// TODO Auto-generated method stub
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
}
private void update() {
// TODO Auto-generated method stub
tv.setText(i+"");
i++;
}
}
```
相信看了上面的代碼,已經(jīng)對Handler的用法有了初步的了解习柠。但是對Handler的用法不僅要知其然匀谣,還要知其所以然。
1.Handler是Android提供的一種異步回調(diào)機制资溃,在Ui線程中使用handler需要new一個handler對象武翎,并重寫其中的handleMessage(Message msg)方法,處理如更新UI等操作溶锭。
從原理上來講宝恶,使用了Hanlder對象的線程需要綁定一個Looper對象,該對象維護(hù)一個消息隊列趴捅,Looper對象取出該隊列的消息后交由handler進(jìn)行處理垫毙。所以在使用Handler時,要調(diào)用Looper.prepare()方法給當(dāng)前的線程綁定一個Looper對象拱绑。
然后調(diào)用loop()建立對消息隊列的循環(huán)综芥,在消息隊列中取出消息后交由相應(yīng)的handler進(jìn)行處理。由于一個線程中可能有多個handler猎拨,為了區(qū)分這些不同的hanlder所需要處理的消息膀藐,每個Message對象都維護(hù)有一個hanlder實例即target征峦,在loop方法中通過調(diào)用msg.target.dispatchMessage(msg)進(jìn)行處理。每個線程只能綁定一個loop對象消请,多個handler共享,handler的在構(gòu)造時從當(dāng)前的線程中取得loop對象类腮,Looper中的myLooper中返回了sThreadLocal.get()所取得的Looper對象臊泰。所以,在使用handler中需要執(zhí)行以下步驟蚜枢,首先調(diào)用Looper.prepare()方法為當(dāng)前線程綁定Looper對象缸逃,然后才可以實例化一個Handler對象,最后調(diào)用loop()方法建立消息循環(huán)厂抽。
2.Handler還有另一個作用就是延遲處理需频。
我們知道在android中,若點擊一個Button后5s沒有反應(yīng)就會出現(xiàn)ANR的問題筷凤。那么此時Hanlder的作用就凸顯出來了昭殉。
如果我們有一個需求,需要程序在點擊一個Button后藐守,6秒后彈出一個Toast挪丢。那么很容易就想到的一個解決辦法就是Thread.sleep(1000*6)。然后在用Handler里彈出來一個Toast卢厂。但是明顯6秒超出了androidUI線程的5秒的限制乾蓬。查看Handler的方法,我們發(fā)現(xiàn)Handler有個Handler.postDelayed(Runnable r, long delayMillis)的方法慎恒。那么這個問題就很容易解決了任内。
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Toast.makeText(MainActivity.this, "延遲處理", Toast.LENGTH_SHORT).show();
}
}, 1000*5);
總結(jié)一下,Handler的兩大用法:1)在子線程中更新主線程融柬;2)延遲處理