在Android項目中經(jīng)常有碰到這樣的問題,在子線程中完成耗時操作之后要更新UI,下面就自己經(jīng)歷的一些項目總結(jié)一下更新的方法。
成都創(chuàng)新互聯(lián)公司專注骨干網(wǎng)絡(luò)服務(wù)器租用十載,服務(wù)更有保障!服務(wù)器租用,德陽電信服務(wù)器托管 成都服務(wù)器租用,成都服務(wù)器托管,骨干網(wǎng)絡(luò)帶寬,享受低延遲,高速訪問。靈活、實現(xiàn)低成本的共享或公網(wǎng)數(shù)據(jù)中心高速帶寬的專屬高性能服務(wù)器。
一. 引言
首先來看一下android中消息機制:
專業(yè)術(shù)語:
Message:消息,其中包含了消息ID,消息處理對象以及處理的數(shù)據(jù)等,由MessageQueue統(tǒng)一列隊,終由Handler處理。?
Handler:處理者,負責Message的發(fā)送及處理。使用Handler時,需要實現(xiàn)handleMessage(Message msg)方法來對特定的Message進行處理,例如更新UI等。?
MessageQueue:消息隊列,用來存放Handler發(fā)送過來的消息,并按照FIFO規(guī)則執(zhí)行。當然,存放Message并非實際意義的保存,而是將Message以鏈表的方式串聯(lián)起來的,等待Looper的抽取。?
Looper:消息泵,不斷地從MessageQueue中抽取Message執(zhí)行。因此,一個MessageQueue需要一個Looper。?
Thread:線程,負責調(diào)度整個消息循環(huán),即消息循環(huán)的執(zhí)行場所。
二. 方法?
1. 用Handler
(1)?主線程中定義Handler:
Java代碼:
[java]?view plain?copy
Handler?mHandler?=?new?Handler()?{ ? ? ?
@Override
public?void?handleMessage(Message?msg)?{
super.handleMessage(msg);
switch?(msg.what)?{
case?0:
span?style="color:#009900;"http://完成主界面更新,拿到數(shù)據(jù)?/span
String?data?=?(String)msg.obj; ? ? ? ? ? ? ? ? ?
updateWeather();
textView.setText(data);
break;
default:
break;
}
}
}; ?
(2)子線程發(fā)消息,通知Handler完成UI更新:
java代碼:
private?void?updateWeather()?{ ? ? ? ? ? ? ? ? ? ? ? ? ?
new?Thread(new?Runnable(){
@Override
public?void?run()?{
span?style="color:#009900;"http://耗時操作,完成之后發(fā)送消息給Handler,完成UI更新;/span
mHandler.sendEmptyMessage(0);
span?style="color:#33cc00;"http://需要數(shù)據(jù)傳遞,用下面方法;??/span
Message?msg?=new?Message();
msg.obj?=?"數(shù)據(jù)";span?style="color:#33cc00;"http://可以是基本類型,可以是對象,可以是List、map等;??/span
mHandler.sendMessage(msg);
}
}).start();
} ?
注意:Handler對象必須定義在主線程中,如果是多個類直接互相調(diào)用,就不是很方便,需要傳遞content對象或通過接口調(diào)用。
2.?用Activity對象的runOnUiThread方法更新
在子線程中通過runOnUiThread()方法更新UI:
java代碼:
new?Thread()?{
public?void?run()?{
span?style="color:#009900;"http://這兒是耗時操作,完成之后更新UI;/span
runOnUiThread(new?Runnable(){
@Override
public?void?run()?{
span?style="color:#009900;"http://更新UI/span
imageView.setImageBitmap(bitmap);
}
});
}
}.start();
如果在非上下文類中,可以通過傳遞上下文實現(xiàn)調(diào)用:
java代碼:
Activity?activity?=?(Activity)?imageView.getContext();
activity.runOnUiThread(new?Runnable()?{ ? ?
@Override
public?void?run()?{
imageView.setImageBitmap(bitmap);
}
});
注意:這種方法使用比較靈活,但如果Thread定義在其他地方,需要傳遞Activity對象。
3.
View.post(Runnable r)
java代碼:
imageView.post(new?Runnable(){
@Override
public?void?run()?{
imageView.setImageBitmap(bitmap);
}
});
這種方法更簡單,但需要傳遞要更新的View過去。
總結(jié):UI的更新必須在主線程中完成,所以不管上述那種方法,都是將更新UI的消息發(fā)送到了主線程的消息對象,讓主線程做處理。
Android
UI多線程Androidthread工作
在一個Android 程序開始運行的時候,會單獨啟動一個Process。默認的情況下,所有這個程序中的Activity或者Service(Service和 Activity只是Android提供的Components中的兩種,除此之外還有Content Provider和Broadcast Receiver)都會跑在這個Process。
一個Android 程序默認情況下也只有一個Process,但一個Process下卻可以有許多個Thread。在這么多Thread當中,有一個Thread,我們稱之為UI Thread。UI Thread在Android程序運行的時候就被創(chuàng)建,是一個Process當中的主線程Main Thread,主要是負責控制UI界面的顯示、更新和控件交互。在Android程序創(chuàng)建之初,一個Process呈現(xiàn)的是單線程模型,所有的任務(wù)都在一個線程中運行。因此,我們認為,UI Thread所執(zhí)行的每一個函數(shù),所花費的時間都應(yīng)該是越短越好。而其他比較費時的工作(訪問網(wǎng)絡(luò),下載數(shù)據(jù),查詢數(shù)據(jù)庫等),都應(yīng)該交由子線程去執(zhí)行,以免阻塞主線程。
那么,UI Thread如何和其他Thread一起工作呢?常用方法是:
誕生一個主線程的Handler物件,當做Listener去讓子線程能將訊息Push到主線程的Message Quene里,以便觸發(fā)主線程的handlerMessage()函數(shù),讓主線程知道子線程的狀態(tài),并在主線程更新UI。
例如,在子線程的狀態(tài)發(fā)生變化時,我們需要更新UI。如果在子線程中直接更新UI,通常會拋出下面的異常:11-07 13:33:04.393: ERROR/JavaBinder(1029):android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.
意思是,無法在子線程中更新UI。為此,我們需要通過Handler物件,通知主線程Ui Thread來更新界面。
如下,首先創(chuàng)建一個Handler,來監(jiān)聽Message的事件:
private final int UPDATE_UI = 1;private Handler mHandler = new MainHandler();private class MainHandler extends Handler {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case UPDATE_UI: {Log.i("TTSDeamon", "UPDATE_UI");showTextView.setText(editText.getText().toString());ShowAnimation();break;}default:break;}}}
或者
private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case UPDATE_UI: {Log.i("TTSDeamon", "UPDATE_UI");showTextView.setText(editText.getText().toString());ShowAnimation();break;}default:break;}}}
當子線程的狀態(tài)發(fā)生變化,則在子線程中發(fā)出Message,通知更新UI。
mHandler.sendEmptyMessageDelayed(UPDATE_UI, 0);
在我們的程序中,很多Callback方法有時候并不是運行在主線程當中的,所以如果在Callback方法中更新UI失敗,也可以采用上面的方法。
照搬一個我自己的回答
zhidao.baidu.com/question/1822980438413918108
public class passWebViewInNewThread{ static android.os.Handler h=null; static WebView w=null; public static void getWebView(WebView w0){//有點想不起來webview的類名是啥了,強答 w=w0; h=new android.os.Handler();//請用UI線程來運行這段代碼 } public static void doWithWebView(){ new Thread() {//開新線程 @Override public void run() { try {//里面寫新線程執(zhí)行內(nèi)容 /* XXX 耗時內(nèi)容…… */ h.post(new Runnable(){//用handler轉(zhuǎn)交UI處理操作 @Override public void run(){ //在這寫操作webview的代碼 //請不要在這里放耗時代碼,否則會卡住UI線程 } }); }catch(Exception e){ } } }.start(); }}//PS:因為我已經(jīng)不記得如果對象聲明在方法內(nèi)部如何用final,所以索性寫成這樣。沒環(huán)境寫JAVA,所以可能有幾個小錯誤
首先,android的UI刷新是在主線程(UI線程)中完成的。四大組件中,activity和service運行在主線程中。現(xiàn)在總結(jié)自己在項目中常用到的UI刷新方式。
第一,利用子線程發(fā)消息刷新UI。
子線程負責處理UI需要的數(shù)據(jù),然后發(fā)消息到主線程來刷新UI。代碼結(jié)構(gòu)如下:
new Thread(new Runnable() {
@Override
public void run() {
Person person=new Person();
person.setName(mName.getText().toString().trim());
person.setPhone(mPhone.getText().toString().trim());
Log.i("person",person.toString());
DatabaseInfoFactory.getPersonDao(mContext).addPerson(person);
Looper.prepare();
Message msg=Message.obtain();
msg.what=0x123456;
handler.sendMessage(msg);
Looper.loop();
}
}).start();
主線程中:
private Handler mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if(msg.what==0x123456||msg.what==0x123){
fillData();
setListener();
}
}
};
第二,利用異步任務(wù)更新UI。代碼結(jié)構(gòu)如下:
new AsyncTaskvoid,void,void() {
@Override
protected void onPostExecute(Void result) {
if(mAdapter==null){
mAdapter=new LeaveInfoAdapter();
//設(shè)置數(shù)據(jù)適配器
mLVleaveInfos.setAdapter(mAdapter);
Log.i("測試", "異步任務(wù)顯示后臺獲得數(shù)據(jù)庫數(shù)據(jù)");
}
else {
mAdapter.notifyDataSetChanged();
}
super.onPostExecute(result);
}
@Override
protected Void doInBackground(Void... params) {
//獲得要顯示的數(shù)據(jù)
mleaveInfos=mLeaveInfosDao.findAll();
if (mleaveInfos==null) {
Toast.makeText(HomeActivity.this,"請假數(shù)據(jù)不存在或是已經(jīng)清除!", 500).show();
}
Log.i("測試", "異步任務(wù)后臺獲得數(shù)據(jù)庫數(shù)據(jù)"+mleaveInfos.size());
return null;
}
}.execute();/void,void,void
第三,利用配置文件+activity的生命周期方法刷新UI。
1、在主線程中啟動一個子線程
首先,我們需要在主線程中啟動一個子線程,這個比較簡單,直接在MainActivity的onCreate()方法中調(diào)用如下方法即可:
new?Thread(mRunnable).start();
2、在子線程中發(fā)送Message給Handler
在創(chuàng)建子線程時,我們使用了Runnable接口對象mRunnable。這里,只需要實現(xiàn)Runnable接口,并重寫該接口的run()方法,在run()方法中實現(xiàn)每1秒發(fā)送一條Message給Handler即可。具體實現(xiàn)方法如下:
/*
*?Function???:???實現(xiàn)run()方法,每1秒發(fā)送一條Message給Handler
*/
private?Runnable?mRunnable?=?new?Runnable()?{
public?void?run()?{
while(true)?{
try?{
Thread.sleep(1000);
mHandler.sendMessage(mHandler.obtainMessage());
}?catch?(InterruptedException?e)?{
e.printStackTrace();
}
}
}
};
3、Handler接收Message通知
最后,我們創(chuàng)建一個Handler對象,用來接收Message通知。在收到Message通知后,完成刷新UI的操作即可。具體實現(xiàn)方法如下:
/*
*?Function???:???實現(xiàn)handleMessage()方法,用于接收Message,刷新UI
*/
private?Handler?mHandler?=?new?Handler()?{
public?void?handleMessage(Message?msg)?{
super.handleMessage(msg);
refreshUI();
}
};
4、刷新UI
由以上的代碼可以看出,刷新UI的操作,我們是放在refreshUI()方法中來完成的。refreshUI()方法的實現(xiàn)也很簡單,調(diào)用HttpUtils工具類中的getInputStream()方法,獲得圖1所示W(wǎng)eb工程的頁面內(nèi)容輸入流,再將該輸入流轉(zhuǎn)化為字符串,放入TextView控件中進行顯示即可。具體實現(xiàn)方法如下:
/*
*?Function???:???刷新UI
*/
private?void?refreshUI()?{
try?{
InputStream?inputStream?=?HttpUtils.getInputStream();
String?resultData?=?HttpUtils.getResultData(inputStream);
mTextView.setText(resultData);
}?catch?(IOException?e)?{
e.printStackTrace();
}
}
分享題目:android子線程更新ui,android任意子線程更新UI
鏈接URL:http://chinadenli.net/article38/dsdsgpp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、定制開發(fā)、品牌網(wǎng)站制作、網(wǎng)站設(shè)計、定制網(wǎng)站、營銷型網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)