Android 在其他线程中更新UI线程的解决方法

本文介绍了在Android应用开发中更新UI的五种方法:利用Handler、runOnUiThread、View.post、Broadcast及AsyncTask。每种方法均有详细的步骤说明及示例代码,帮助开发者了解如何在不同场景下高效地更新UI。
Android中消息机制: 

 
引用

Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。 
Handler:处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。 
MessageQueue:消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。 
Looper:消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。 

Thread:线程,负责调度整个消息循环,即消息循环的执行场所。 

方法一:用Handler 

1、主线程中定义Handler: 

Java代码  收藏代码
  1. Handler mHandler = new Handler() {  
  2.   
  3.         @Override  
  4.         public void handleMessage(Message msg) {  
  5.             super.handleMessage(msg);  
  6.             switch (msg.what) {  
  7.             case 0:  
  8.                 //完成主界面更新,拿到数据  
  9.                 String data = (String)msg.obj;  
  10.                   
  11.                 updateWeather();  
  12.                 textView.setText(data);  
  13.                 break;  
  14.             default:  
  15.                 break;  
  16.             }  
  17.         }  
  18.   
  19.     };  


2、子线程发消息,通知Handler完成UI更新: 
Java代码  收藏代码
  1. private void updateWeather() {  
  2.           
  3.           
  4.         new Thread(new Runnable(){  
  5.   
  6.             @Override  
  7.             public void run() {  
  8.                 //耗时操作,完成之后发送消息给Handler,完成UI更新;  
  9.                 mHandler.sendEmptyMessage(0);  
  10.                   
  11.                 //需要数据传递,用下面方法;  
  12.                 Message msg =new Message();  
  13.                 msg.obj = "数据";//可以是基本类型,可以是对象,可以是List、map等;  
  14.                 mHandler.sendMessage(msg);  
  15.             }  
  16.               
  17.         }).start();  
  18.           
  19.     }  

方法一的Handler对象必须定义在主线程中,如果是多个类直接互相调用,就不是很方便,需要传递content对象或通过接口调用; 

方法二:用Activity对象的runOnUiThread方法更新 
在子线程中通过runOnUiThread()方法更新UI: 
Java代码  收藏代码
  1. new Thread() {  
  2.             public void run() {  
  3.                 //这儿是耗时操作,完成之后更新UI;  
  4.                 runOnUiThread(new Runnable(){  
  5.   
  6.                     @Override  
  7.                     public void run() {  
  8.                         //更新UI  
  9.                         imageView.setImageBitmap(bitmap);  
  10.                     }  
  11.                       
  12.                 });  
  13.             }  
  14.         }.start();  

如果在非上下文类中(Activity),可以通过传递上下文实现调用; 
Java代码  收藏代码
  1. Activity activity = (Activity) imageView.getContext();  
  2.                 activity.runOnUiThread(new Runnable() {  
  3.   
  4.                     @Override  
  5.                     public void run() {  
  6.                         imageView.setImageBitmap(bitmap);  
  7.                     }  
  8.                 });  

这种方法使用比较灵活,但如果Thread定义在其他地方,需要传递Activity对象; 

方法三:View.post(Runnable r) 

子线程如果持有某个View的引用,要对该View进行更新,则可调用该View对象的post(Runnable r)或postDelay(Runnable r)方法

Handler对象也有post()方法。其实在Android的源码中,这些post()方法都是借助下面的第3种方法:Handler + Message来实现的。



Java代码  收藏代码
  1. imageView.post(new Runnable(){  
  2.   
  3.                     @Override  
  4.                     public void run() {  
  5.                         imageView.setImageBitmap(bitmap);  
  6.                     }  
  7.                       
  8.                 });  

4、Broadcast

子线程中发送广播,主线程中接收广播并更新UI

5、AsyncTask

AsyncTask可方便地实现新开一个线程,并将结果返回给UI线程,而不需要开发者手动去新开一个线程,也无须开发者使用Handler,非常方便。

应当注意的是AsyncTask是一个抽象类,其三个泛型参数的意义如下:
AsyncTask<Param, Progress, Result>
Param:发送给新开的线程的参数类型
Progress:表征任务处理进度的类型。
Result:线程任务处理完之后,返回给UI线程的值的类型。

该类中有四个抽象函数,onPreExecute(), doInBackground(Params... param),
onProgressUpdate(Progress... progress), onPostExecute(Result result)。
除了,doInBackground(Params...)方法,其它三个方法都运行在UI线程。

自定义一个类继承AsyncTask并至少实现doInBackground()函数。在该函数中执行的过程中,可以随时调用publishProgress(Progress...)报告其执行进度。此时会触发另一个方法onProgressUpdate(Progress... progress),以便在UI线程中的某些控件(如ProgressBar)上更新任务处理的进度。

也可以等doInBackground()执行完,进入onPostExecute()方法后,再进行UI控件的更新。

可在任意时间,任意线程中,取消AsyncTask开启的任务(调用自定义的AsynTask子类的cancel(boolean mayInterruptIfRunning)方法)

使用示例如下:

//如果没记错的话,这个例子应该是之前总结的时候从官网剪下来的

public void onClick(View v) {
   new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
   /** The system calls this to perform work in a worker thread and
     * delivers it the parameters given to AsyncTask.execute() */
   protected Bitmap doInBackground(String... urls) {
       return loadImageFromNetwork(urls[0]);
   }
   
   /** The system calls this to perform work in the UI thread and delivers
     * the result from doInBackground() */
   protected void onPostExecute(Bitmap result) {
       mImageView.setImageBitmap(result);
   }
}


这种方法更简单,但需要传递要更新的View过去; 
总结:UI的更新必须在主线程中完成,所以不管上述那种方法,都是将更新UI的消息发送到了主线程的消息对象,让主线程做处理;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值