《代碼里的世界》
用文字札記描繪自己 android學習之路
轉(zhuǎn)載請保留出處 by Qiao http://blog.csdn.net/qiaoidea/article/details/45115047
Android更新Ui進階精解(一) android ui線程檢查機制
Android更新Ui進階精解(二) android 線程更新UI機制
1. 簡述
先貼一個我們剛做Android開發(fā)時候最容易遇到的一個錯誤異常 AndroidRuntimeException :“Only the original thread that created a view hierarchy can touch its views”
具體原因是當我們在對ui做出更改時燥爷,Android檢查我們當前的操作線程是否為UI線程压储,若不是即報該異常忘古。(詳見 ViewRootImpl類的checkThread方法)。
2. 方案
那么我們該如何更新Ui呢疾渴,這里簡要使用和講述的是Handler。先簡述概念:Adroid在運行時會創(chuàng)建一個UiThread的主線程來負責控制UI界面的顯示屯仗、更新和控件交互搞坝。其他線程則通過handler將更新邏輯等消息事件(Message) push到主線程的消息隊列(MessageQueue),最后由主線程有序地處理這些消息事件(handleMessage)魁袜,實現(xiàn)對界面的更新和控制桩撮。
本文主要講述更新UI的方法和使用技巧,關(guān)于更新過程的原理內(nèi)容請關(guān)注后邊的 Android更新Ui進階峰弹。
其中店量,常用的幾種方法簡單概括有:
- handler.sendMessage();
- handler.post();
- 在activity中可以 runOnUiThread();
- 在子view中可以 view.post()
其實后邊幾種方法整個流程處理邏輯基本也是基于第一種,封裝成消息事件對象發(fā)送至隊列并被有序處理而已鞠呈,只是android給我們封裝好了方法融师。
3. 實踐
3.1 handler.senMessage() + handler.dispatchMessage()
在activity中定義主線程的handler(子線程同樣可以定義handler,當然 需要 消息泵looper來輪訓)蚁吝,實現(xiàn)dispatchMessage方法處理message對象旱爆。該對象是在子線程處理邏輯完成之后,將內(nèi)容封裝成消息窘茁,通過handler.senMessage()發(fā)送該消息怀伦。
示例代碼:
a. 首先定義好更改ui的代碼邏輯 handler和dispatchMessage方法
/**
* 通過message攜帶結(jié)果數(shù)據(jù)變更ui
* 先定義主線程handler和更新數(shù)據(jù)的方法
*/
private static final int UPDATE_TITLE = 0; //更新標題的標志
private Handler mainHandler= new Handler()
{
public void dispatchMessage(android.os.Message msg) {
if(msg.what == UPDATE_TITLE){
String title = msg.getData().getString("Result");
titleView.setText(title);
}else{
//其他消息
}
};
};
b. 需要處理數(shù)據(jù)的部分,比如某些耗時操作山林,放在子線程中執(zhí)行房待,之后調(diào)用handler.senMessage()
new Thread(){
@Override
public void run() {
//你的處理邏輯,這里簡單睡眠一秒
this.sleep(1000);
//通知更新UI
//Message msg = new Message();
//我們可以使用構(gòu)造方法來創(chuàng)建Message,但出于節(jié)省內(nèi)存資源的考量驼抹,我們應該使用Message.obtain()從消息池中獲得空消息對象
Message msg = mainHandler.obtainMessage();
msg.what = UPDATE_TITLE;
Bundle bundle = new Bundle();//消息數(shù)據(jù)實體
bundle.putString("Result", "sendMsg——Result");
msg.setData(bundle);
//mainHandler.sendMessage(msg);
//如果使用的是Message.obtain()我們可以直接
msg.sendToTarget();
}
}.start();
這里提下該消息對象Message (更多詳見Android更新Ui進階)
可用于傳數(shù)據(jù)的參數(shù):
- int what;
- int arg1;
- int arg2;
- Object obj;
- Bundle data;
用于回調(diào)的
- Runnable callback桑孩;
通常為了使代碼邏輯看起來更清晰易于理解,我更傾向于通過直接傳回調(diào)callback 來更新Ui(方案∩氨巍2/3/4)洼怔。針對方案一,可以簡單優(yōu)化為
直接定義handler
private Handler mainHandler= new Handler();
發(fā)送消息
new Thread(){
@Override
public void run() {
try {
//你的處理邏輯,這里簡單睡眠一秒
this.sleep(1000);
//利用obtain
msg = Message.obtain(mainHandler, new Runnable() {
@Override
public void run() {
titleView.setText("sendMsg——Result");
}
});
msg.sendToTarget();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
3.2 handler.post()
對于方案一的第二種方法左驾,又可以簡化為
new Thread(){
@Override
public void run() {
try {
//你的處理邏輯,這里簡單睡眠一秒
this.sleep(1000);
mainHandler.post(new Runnable() {
@Override
public void run() {
//你的處理邏輯
titleView.setText("postRunnable——Result");
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
3.3 runOnUiThread()
對于上邊的方案镣隶,在activity中又可以進一步簡化,不用new Handler對象诡右,直接調(diào)用runOnUiThread()方法
/**
* 使用activity的runOnUiThread
*/
new Thread(){
@Override
public void run() {
try {
//你的處理邏輯,這里簡單睡眠一秒
this.sleep(1000);
runOnUiThread(new Runnable() {
@Override
public void run() {
titleView.setText("runOnUiThread——Result");
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
3.4 view.post()
當然安岂,如果有子view或自定義view,處理邏輯后可以直接用view.post()方法
new Thread(){
@Override
public void run() {
try {
//你的處理邏輯,這里簡單睡眠一秒
this.sleep(1000);
viewPostBtn.post(new Runnable() {
@Override
public void run() {
titleView.setText("viewPost——Result");
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
綜上帆吻,更新view看起來就不是那么難使用了域那。。
最后附上源代碼文件。
點擊下載源碼