Android子线程更新UI

1、子线程更新UI的方法:

1.1、子线程调用Handler的sendMessage(message)或者post(runnable)发送事件:
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发送消息,  
          }  
    }

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

1.2、Activity.runOnUiThread(Runnable ):
  • 如果当前线程是UI线程,那么行动是立即执行。如果当前线程不是UI线程,操作是发布到事件队列的UI线程。
  • 如果Thread定义在其他地方,需要传递Activity对象(通过构造函数传递)。
new Thread() {
                public void run() {
                    //这儿是耗时操作,完成之后更新UI;
                   runOnUiThread(new Runnable(){
                        @Override
                        public void run() {
                            //更新UI
                          imageView.setImageBitmap(bitmap);
                           }
                    });
             }
    }.start();
1.3、View.post(Runnable ):
  • 从Runnable派生你的子类,重载run()方法。然后调用View.post(myRunnableObj)即可把你的Runnable对象增加到UI线程中运行。
  • 注意:post函数,里面传递的是一个runnable 接口(你懂得 runnable 可不是一个线程这个你一定要和thread 区分开) 。
public void onClick( View v ) {  
        new Thread( new Runnable() {  
            public void run() {  
                // 耗时操作 
             loadNetWork();  
          myText.post( new Runnable() {  
                                myText.setText( 来自网络的信息);  
                    });  
                }  
        }).start();  
    }
1.4、使用异步任务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 ); //得到来自网络的信息刷新页面   
       }  
    }
1.5、应用场合:
  • 如果是后台任务,像是下载任务等,就需要使用AsyncTask。
  • 如果需要传递状态值等信息,像是蓝牙编程中的socket连接,就需要利用状态值来提示连接状态以及做相应的处理,就需要使用Handler + Thread的方式;
  • 需要另开线程处理数据以免阻塞UI线程,像是IO操作或者是循环,可以使用Activity.runOnUiThread();
  • 如果只是单纯的想要更新UI而不涉及到多线程的话,使用View.post()就可以了;

2、子线程为啥不能更新UI:

2.1、场景:
  • 我们布局里有一个TextView、一个button,点击button,启动一个子线程,在子线程中更改textview 的显示内容,发现程序崩溃,报如下错误:
    android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6357)
    at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:874)

  • 在 framework/base/core/java/android/view/ViewRootImpl.java 中有一个方法 checkThread ,源码如下:

    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }
    mThread是主线程,在应用程序启动的时候,就已经被初始化了。
2.2、原理:
  • ViewRootImpl的创建在onResume方法回调之后。
  • 因为ViewRootImpl在主线程中创建,所以我们要在主线程中更新UI。同理,如果ViewRootImpl在子线程中创建的话,那么也可以在子线程中更新UI,也就是说在哪里更新UI和ViewRootImpl在哪里创建是关联的。默认ViewRootImpl在主线程中创建。
2.3、笼统说法:
  • Android的UI访问是没有加锁的,这样在多个线程访问UI是不安全的。所以Android中规定只能在UI线程中访问UI。
  • Android实现View更新有两组方法,分别是invalidate和postInvalidate。前者在UI线程中使用,后者在非UI线程即子线程中使用。换句话说,在子线程调用 invalidate 方法会导致线程不安全。熟悉View工作原理的人都知道,invalidate 方法会通知 view 立即重绘,刷新界面。作一个假设,现在我用 invalidate 在子线程中刷新界面,同时UI线程也在用 invalidate 刷新界面,这样会不会导致界面的刷新不能同步?这就是invalidate不能在子线程中使用的原因。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值