android中子线程更新UI的方式浅析




android中子线程更新UI的方式浅析
2015-05-25       0  个评论    来源:安诺爱成长  
收藏     我要投稿

一、为何写作此文

??你是不是经常看到很多书籍中说:不能在子线程中操作ui,不然会报错。你是不是也遇到了如下的疑惑(见下面的代码):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<code class = "hljs java" > @Override 
     protected void onCreate(Bundle savedInstanceState) { 
         super .onCreate(savedInstanceState); 
         setContentView(R.layout.activity_main); 
         tv = (TextView) findViewById(R.id.tv); 
         Thread.currentThread().setName( "UIThread" ); 
         new LooperThread().start(); 
    
 
     private class LooperThread extends Thread { 
 
         @Override 
         public void run() { 
             Thread.currentThread().setName( "OtherThread" ); 
             tv.setText( "other thread" ); 
        
     }  </code>

??上面确实在子线程中操作ui了,但是他并不会报错,为什么呢?这不是跟书上的说法刚好相悖吗?当时自己也是遇到了这个问题,所以有了这篇博客,感谢网络上的那些前辈们的无私分享,现将自己的整理和思考记录下来。

二、引入

??在Android开发过程中,常需要更新界面的UI。而更新UI是要主线程来更新的,即UI线程更新。如果在主线线程之外的线程中直接更新页面显示常会报错。抛出异常:android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.怎么解决呢?下面我会详细列出子线程更新ui的方法:

三、子线程更新UI的方法

1、用Handler+message

??主线程中定义Handler,子线程发消息,通知Handler完成UI更新。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<code class = "hljs java" >mHandler = new Handler() { 
    @Override 
    public void handleMessage(Message msg) { 
         //操作界面 
        myText.setText( 来自网络的信息); 
       super .handleMessage(msg); 
   
   }; 
public class MyThread extends Thread { 
      public void run() { 
?    ?    ?    // 耗时操作 
?    ?    ?    ?loadNetWork(); 
             Message msg = new Message(); 
             mHandler.sendMessage(msg); //向Handler发送消息, 
      
  }</code>

handler的原理图如下:

这里写图片描述

2、用runOnUiThread更新

??这个最好用, 凡是要刷新页面的地方,都可以按照如下方式写。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<code class = "hljs java" > new Thread() {
             public void run() {
                 //这儿是耗时操作,完成之后更新UI;
                 runOnUiThread( new Runnable(){
 
                     @Override
                     public void run() {
                         //更新UI
                         imageView.setImageBitmap(bitmap);
                     }
 
                 });
             }
         }.start();</code>

??这种方法使用比较灵活,但如果Thread定义在其他地方,需要传递Activity对象(通过构造函数传递)。

3、View.post(Runnable r)

??方法解释:从Runnable派生你的子类,重载run()方法。然后调用View.post(myRunnableObj)即可把你的Runnable对象增加到UI线程中运行。

?
1
2
3
4
5
6
7
8
9
10
11
<code class = "hljs cs" > public void onClick( View v ) { 
         new Thread( new Runnable() { 
                 public void run() { 
                        // 耗时操作 
?    ?    ?    ?    ?    ?      loadNetWork(); 
?                          myText.( new Runnable() { 
                                   myText.setText( 来自网络的信息); 
                           }); 
                 
         }).start(); 
}</code>

??这种方法更简单,但需要传递要更新的View过去。注意:post函数,里面传递的是一个runnable 接口(你懂得 runnable 可不是一个线程这个你一定要和thread 区分开) 。

4、使用异步任务

?
1
2
3
4
5
6
7
8
9
10
11
12
<code class = "hljs java" > //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 ); //得到来自网络的信息刷新页面  
 
   
}</code>

这里写图片描述

应用场合

如果是后台任务,像是 下载任务等,就需要使用AsyncTask。 如果需要传递状态值等信息,像是蓝牙编程中的socket连接,就需要利用状态值来提示连接状态以及做相应的处理,就需要使用Handler + Thread的方式; 需要另开线程处理数据以免阻塞UI线程,像是IO操作或者是循环,可以使用Activity.runOnUiThread(); 如果只是单纯的想要更新UI而不涉及到多线程的话,使用View.post()就可以了;

四、在子线程中更新了UI的错觉

??回到开头的问题,子线程更新ui成功了,其实不然。还有另外一种错误的方法:在子线程中使用接口回调,在activity中实现该方法来更新ui,其实这个方法也是变相的在子线程中更新了UI。为什么成功了呢?原因精炼点说就是:这个异常是android源码中的检测设定抛出的,如果检测的方法没有执行就不会报错。onCreate方法里开线程更新UI不报错,是因为view还没有还出来呢,没有调用invalidate方法。

更深入的解释请参考:

http://www.2cto.com/kf/201111/111172.html
http://blog.csdn.net/imyfriend/article/details/6877959
http://doc.okbase.net/aigestudio/archive/127460.html
http://blog.csdn.net/zhaokaiqiang1992/article/details/43410351
http://blog.csdn.net/aigestudio/article/details/43449123
http://javapolo.iteye.com/blog/1343583
http://blog.csdn.net/androidzhaoxiaogang/article/details/8136222

五、综述

??有的时候使用子线程来直接更新ui,并不会报错,但并不推荐这么做,google的android底层代码中会对更新ui的线程做检测,原因就是为了避免我们在非ui线程中直接更新ui。检测针对两个方面:1.是否更新了ui,更新view在android中对应的方法是invalidate。2.更新时当前线程是否是ui线程。虽然我们钻空子,可以不报异常,但是这并不是好的方式。google这样设计的原因就在于让UI线程做的事情更纯粹一些,都是界面方面的事情,如果在ui线程执行耗时的操作,在做UI操作的时候会有卡顿的感觉。即从更新View的角度来说,最好是UI线程,非UI线程也不是不能更新UI。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值