线程中更新ui方法汇总
一、为何写作此文
你是不是经常看到很多书籍中说:不能在子线程中操作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 ); //得到来自网络的信息刷新页面
}
}
应用场合
- 如果是后台任务,像是下载任务等,就需要使用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。
线程中更新ui方法汇总的更多相关文章
- Android在子线程中更新UI(二)
MainActivity如下: package cc.testui2; import android.os.Bundle; import android.view.View; import andro ...
- Android在子线程中更新UI(一)
MainActivity如下: package cc.testui1; import android.os.Bundle; import android.os.Handler; import andr ...
- Android开之在非UI线程中更新UI
当在非UI线程中更新UI(程序界面)时会出现例如以下图所看到的的异常: 那怎样才干在非UI线程中更细UI呢? 方法有非常多种.在这里主要介绍三种: 第一种:调用主线程mHandler的post(Run ...
- android 不能在子线程中更新ui的讨论和分析
问题描写叙述 做过android开发基本都遇见过 ViewRootImpl$CalledFromWrongThreadException,上网一查,得到结果基本都是仅仅能在主线程中更改 ui.子线程要 ...
- 使用Handler在子线程中更新UI
Android规定仅仅能在主线程中更新UI.假设在子线程中更新UI 的话会提演示样例如以下错误:Only the original thread that created a view hierach ...
- Android多线程之(一)View.post()源码分析——在子线程中更新UI
提起View.post(),相信不少童鞋一点都不陌生,它用得最多的有两个功能,使用简便而且实用: 1)在子线程中更新UI.从子线程中切换到主线程更新UI,不需要额外new一个Handler实例来实现. ...
- 如何在子线程中更新UI
一:报错情况 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that creat ...
- C# WINFORM 线程中更新UI
幸好今天是周末,有时间把这个问题记录一下.在多种语言之间切换,发现开发效率降的很低了,开发成本都集中到调式上了,C/C++这些放弃很久了,突然感觉线程这个问题搞的有点烦躁 我这里提到的线程中更新UI, ...
- 老问题:Android子线程中更新UI的3种方法
在Android项目中经常有碰到这样的问题,在子线程中完成耗时操作之后要更新UI,下面就自己经历的一些项目总结一下更新的方法: 方法一:用Handler 1.主线程中定义Handler: Handle ...
随机推荐
- canvas雪花特效-jQuery插件实现
这是一款效果十分逼真的html5 canvas下雪场景动画特效插件.这款下雪特效是基于Jason Brown的Snowfall jquery plugin的基础上制作的.在Snowfall jquer ...
- MySQL-基本概念
一.Mysql逻辑架构 引用自<高性能Mysql> 二.并发控制 读写锁:读锁是共享的,写锁是排他的,会阻塞其他的写锁和读锁. 锁粒度:表锁.行级锁 三.事务 事务(ACID特性):原子性 ...
- SharePoint Server和Office 365之间的混合模式集成概述
正如您可能已经知道的那样,云中的Microsoft Office 365和SharePoint Server 2013/2016内部部署可以通过多种方式协同工作.这些通常被称为混合模式,因为它们将功能 ...
- JavaScript_4_数据类型
1. JavaScript对大小写敏感. 2. JavaScript是脚本语言.浏览器会在读取代码时,逐行地执行脚本代码.而对于传统编程来说,会在执行前对所有代码进行编译. 3. 变量什么用var, ...
- 在openSUSE 13.1上用gem安装rails无反应: gem install rails
解决方案: gem install rails -V ....其实他本身在后台运行,白白的给他中断好多次,用-V这个选项就可以直接回显信息了
- ProtoBuff3 unity_TCP网络发包解包&&消息订阅
using Google.Protobuf; //using Google.Protobuf.Examples.AddPerson; using Google.Protobuf.WellKnownTy ...
- CDOJ 490 UESTC 490 Swap Game(思路,逆序对)
题意:有两种颜色的小球形成环,求最小交互次数使球相连. 题解:先解决另一个简单的问题,如果是一个链,把红球标记为1,蓝球标记为0,要排成升序需要多少次交换呢?答案是逆序对总数,原因是一次交互最多消除一 ...
- [学习笔记] SSD代码笔记 + EifficientNet backbone 练习
SSD代码笔记 + EifficientNet backbone 练习 ssd代码完全ok了,然后用最近性能和速度都非常牛的Eifficient Net做backbone设计了自己的TinySSD网络 ...
- [CV笔记]图像特征提取三大法宝:HOG特征,LBP特征,Haar特征
(一)HOG特征 1.HOG特征: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子.它通过计算和 ...
- Unity调用Windows窗口句柄,选择文件和目录
T:2019-6-25 10:06:59 C:Scatt Kang using System; using System.Collections; using System.Collections.G ...