大家听到的往往都是子线程中不能更新UI,尤其是培训班出来的学生,听惯了老师说“子线程中不能更新ui啊”,这样的老师我只能说不负责任,今天我要讲的是在子线程中更新ui的几种方法

方法一:用Handler
1、主线程中定义Handler:

Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
            case 0:
                //完成主界面更新,拿到数据
                String data = (String)msg.obj;
                updateWeather();
                textView.setText(data);
                break;
            default:
                break;
            }
        }
    };

2、子线程发消息,通知Handler完成UI更新:

private void updateWeather() {
        new Thread(new Runnable(){
            @Override
            public void run() {
                //耗时操作,完成之后发送消息给Handler,完成UI更新;
                mHandler.sendEmptyMessage(0);
                //需要数据传递,用下面方法;
                Message msg =new Message();
                msg.obj = "数据";//可以是基本类型,可以是对象,可以是List、map等;
                mHandler.sendMessage(msg);
            }
        }).start(); 
    }

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

方法二:用runOnUiThread更新

new Thread() {
            public void run() {
                //这儿是耗时操作,完成之后更新UI;
                runOnUiThread(new Runnable(){
                    @Override
                    public void run() {
                        //更新UI
                        imageView.setImageBitmap(bitmap);
                    }
                });
            }
        }.start();

如果在非上下文类中(Activity),可以通过传递上下文实现调用;

ctivity activity = (Activity) imageView.getContext();
                activity.runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        imageView.setImageBitmap(bitmap);
                    }
                });

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

方法三:View.post(Runnable r)

imageView.post(new Runnable(){
                    @Override
                    public void run() {
                        imageView.setImageBitmap(bitmap);
                    }

                });

这种方法更简单,但需要传递要更新的View过去;

方法四:使用AsyncTask

//UI线程中执行  
new DownloadImageTask().execute( "www.91dota.com" );  
private class DownloadImageTask extends AsyncTask {  
    protected String doInBackground( String... url ) {  
         return loadDataFormNetwork( url[0] );//后台耗时操作  
    }  

    protected void onPostExecute( String result ) {  
          myText.setText( result ); //得到来自网络的信息刷新页面   

   }  
}

大家对于这一种在子线程中更新UI的操作可能比较熟悉。。

方法五:使用Looper类

  1. 什么是Looper?
    Android中的Looper类,是用来封装消息循环和消息队列的一个类,用于在android线程中进行消息处理。
  2. 怎样工作?
    (1) Looper类用来为一个线程开启一个消息循环。 默认情况下android中新诞生的线程是没有开启消息循环的。(主线程除外,主线程系统会自动为其创建Looper对象,开启消息循环。) Looper对象通过MessageQueue来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。
    (2) 通常是通过Handler对象来与Looper进行交互的。Handler可看做是Looper的一个接口,用来向指定的Looper发送消息及定义处理方法。 默认情况下Handler会与其被定义时所在线程的Looper绑定,比如,Handler在主线程中定义,那么它是与主线程的Looper绑定。 mainHandler = new Handler() 等价于new Handler(Looper.myLooper()). Looper.myLooper():获取当前进程的looper对象,类似的 Looper.getMainLooper() 用于获取主线程的Looper对象。
    (3)在非主线程中直接new Handler() 会报如下的错误: E/AndroidRuntime( 6173): Uncaught handler: thread Thread-8 exiting due to uncaught exception E/AndroidRuntime( 6173): java.lang.RuntimeException: Can’t create handler inside thread that has not called Looper.prepare() 原因是非主线程中默认没有创建Looper对象,需要先调用Looper.prepare()启用Looper。
    (4) Looper.loop(); 让Looper开始工作,从消息队列里取消息,处理消息。
  3. 如何在子线程中更新UI
class LooperThread extends Thread  {  
   public Handler mHandler;  
   public void run()   {  
     Looper.prepare();  
     mHandler = new Handler(){  

public void handleMessage(Message msg){  
// process incoming messages here  
  }  
};  
     Looper.loop();  
}

如果线程中使用Looper.prepare()和Looper.loop()创建了消息队列就可以让消息处理在该线程中完成