一、为何写作此文

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

@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");
}
}

  上面确实在子线程中操作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更新。

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的原理图如下:

2、用runOnUiThread更新

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

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

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

3、View.post(Runnable r)

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

public void onClick( View v ) {
new Thread( new Runnable() {
public void run() {
// 耗时操作
​ ​ ​ ​ ​ ​ loadNetWork();
​ myText.( new Runnable() {
myText.setText( 来自网络的信息);
});
}
}).start();
}

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

4、使用异步任务

//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. 如果是后台任务,像是下载任务等,就需要使用AsyncTask。
  2. 如果需要传递状态值等信息,像是蓝牙编程中的socket连接,就需要利用状态值来提示连接状态以及做相应的处理,就需要使用Handler + Thread的方式;
  3. 需要另开线程处理数据以免阻塞UI线程,像是IO操作或者是循环,可以使用Activity.runOnUiThread();
  4. 如果只是单纯的想要更新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。

线程中更新ui方法汇总的更多相关文章

  1. Android在子线程中更新UI(二)

    MainActivity如下: package cc.testui2; import android.os.Bundle; import android.view.View; import andro ...

  2. Android在子线程中更新UI(一)

    MainActivity如下: package cc.testui1; import android.os.Bundle; import android.os.Handler; import andr ...

  3. Android开之在非UI线程中更新UI

    当在非UI线程中更新UI(程序界面)时会出现例如以下图所看到的的异常: 那怎样才干在非UI线程中更细UI呢? 方法有非常多种.在这里主要介绍三种: 第一种:调用主线程mHandler的post(Run ...

  4. android 不能在子线程中更新ui的讨论和分析

    问题描写叙述 做过android开发基本都遇见过 ViewRootImpl$CalledFromWrongThreadException,上网一查,得到结果基本都是仅仅能在主线程中更改 ui.子线程要 ...

  5. 使用Handler在子线程中更新UI

    Android规定仅仅能在主线程中更新UI.假设在子线程中更新UI 的话会提演示样例如以下错误:Only the original thread that created a view hierach ...

  6. Android多线程之(一)View.post()源码分析——在子线程中更新UI

    提起View.post(),相信不少童鞋一点都不陌生,它用得最多的有两个功能,使用简便而且实用: 1)在子线程中更新UI.从子线程中切换到主线程更新UI,不需要额外new一个Handler实例来实现. ...

  7. 如何在子线程中更新UI

    一:报错情况 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that creat ...

  8. C# WINFORM 线程中更新UI

    幸好今天是周末,有时间把这个问题记录一下.在多种语言之间切换,发现开发效率降的很低了,开发成本都集中到调式上了,C/C++这些放弃很久了,突然感觉线程这个问题搞的有点烦躁 我这里提到的线程中更新UI, ...

  9. 老问题:Android子线程中更新UI的3种方法

    在Android项目中经常有碰到这样的问题,在子线程中完成耗时操作之后要更新UI,下面就自己经历的一些项目总结一下更新的方法: 方法一:用Handler 1.主线程中定义Handler: Handle ...

随机推荐

  1. If people in the communications only think about gains and losses of interest, then the pleasure of knowing each other will cease to exist.

    If people in the communications only think about gains and losses of interest, then the pleasure of ...

  2. so文件动态加载注意事项

    动态加载是指将so文件存放于服务器,在需要用的时候,通过服务器下载到本地,然后加载. 需要注意的: 手机cpu架构,不同的架构运行不同的so 解决方法: 1,欺骗性: 如果so架构不全,就在apk打包 ...

  3. 本号讯 | 永不消失的协作“空间站”开课;微软推出微软云Azure文档网站

    8月29日,针对企业常面临的“协同办公”困难,开展以“还有这种操作?永不消失的协作'空间站'”为主题的协同办公培训课. 课程内容包含:在Office 365环境中,如何利用Teams与Groups等功 ...

  4. Python3+Selenium3+webdriver学习笔记11(cookie处理)

    #!/usr/bin/env python# -*- coding:utf-8 -*-'''Selenium3+webdriver学习笔记11(cookie处理)'''from selenium im ...

  5. Cookie 没你不行

    Cookie 没你不行 Cookie 没你不行 前言: Cookie 是什么 起源 到底是什么? 使用场景 如何使用cookie Cookie 和http协议 (服务端操作cookie) Cookie ...

  6. 不写画面的网页程序设计,Web API、Web Service、WCF Service

    客户有一个系统,经常要连上我方,查询数据 以前的作法是给对方一个账号,让他可以连上我们的DB来查询. 所以,早期的同仁,真的给他们DB链接字符串 客户的Windows程序.网站就真的靠这么危险的方式, ...

  7. python爬虫之路——无头浏览器初识及简单例子

    from selenium import webdriver url='https://www.jianshu.com/p/a64529b4ccf3' def get_info(url): inclu ...

  8. Codeforces Round #320 (Div. 1) [Bayan Thanks-Round] C A Weakness and Poorness (三分)

    显然f(x)是个凹函数,三分即可,计算方案的时候dp一下.eps取大了会挂精度,指定循环次数才是正解. #include<bits/stdc++.h> using namespace st ...

  9. 文件 MD5 SHA1 SHA256 SHA512 校验码生成工具 V1.3

    [程序介绍]免费开源的 文件 MD5 SHA1 SHA256 SHA512 校验码生成工具 V1.3 这是一个有意思的程序,同一个程序,即是图形程序,又是命令行程序.程序作用:输入一个文件的路径,输出 ...

  10. Hibernate中session的save方法问题

    今天在使用session.save(),进行插入数据操作时,一直没有成功,也没有报错.后来发现是因为没有创建事务,提交事务的原因 你对flush和commit的意思没有理解到:1,flush代表刷新, ...