在Android的子线程去更新UI的内容,会导致不确定的异常。

因为Android有个模式是,单一线程模型:Android UI工具箱(toolkit)不是一个线程安全的,并且它总是被放在主线程上操作。

public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork();
mImageView.setImageBitmap(b);
}
}).start();
}

首先,从上面代码看上去好像非常完美,因为它不会阻塞UI线程,不幸的是,它违背了单一线程模型:Android UI工具箱(toolkit)不是一个线程安全的,并且它总是被放在主线程上操作。

这个ImageView被一个工作线程操作,这导致非常不可思议的问题。跟踪和修复这样一个bug很难并且也耗时。
Android提供了几种从其他线程访问主线程的方式:
  1. Activity.runOnUiThread(Runnable)
  2. View.post(Runnable)
  3. View.postDelayed(Runnable, long)
  4. Handler

以上任何一种方式都能修正我们的代码:

public void onClick(View v) {
new Thread(new Runnable() {
public void run() {
final Bitmap b = loadImageFromNetwork();
mImageView.post(new Runnable() {
public void run() {
mImageView.setImageBitmap(b);
}
});
}
}).start();
}

当然我们还有更简单的方法,使用AsyncTask

不幸的是,这些类和方法导致我们的代码变得复杂和可读性差。当你实现复杂的操作来频繁的更新界面,使用这种方式变得更加糟糕。为了解决这个问题,Android1.5提供了一个公共类叫做AsyncTask,它简化了任务线程和主线程之间的通信。
 
        在Android1.0和1.1也可使用AsyncTask只不过它的名字为UserTask。
        AsyncTask的目的就是帮助你管理线程。我们之前的例子很容易被改写如下形式:
public void onClick(View v) {
new DownloadImageTask().execute("http://example.com/image.png");
} private class DownloadImageTask extends AsyncTask<string, integer, Bitmap> {
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
} protected void onPostExecute(Bitmap result) {
mImageView.setImageBitmap(result);
}
}
AsyncTask通过它的子类才能使用。要记住,一个AsyncTask实例必须在主线程创建并且只能被执行一次。完全理解和使用这个类,你可以阅读AsyncTask文档。这里快速的说一下AsyncTask是怎么工作的:
1>可以通过泛型指定它的类型:参数,进度值,任务的结果值。
2>doInBackGround()方法自动在工作线程中只想能够。
3>onPreExecute(),onPostExecute(),onProgressUpdate()方法都在UI线程中执行。
4>doInBackground()方法返回的值被当作参数传递给onPostExecute()方法。
5>你能够在doInBackground()方法里任何时候调用publishProgress()方法在UI线程中去执行onProgressUpdate()方法。
 
下面写一个重载方法比较多得类:
 package vic.wong.main;
import android.os.AsyncTask;
import android.widget.ProgressBar;
import android.widget.TextView; /**
* 生成该类的对象,并调用execute方法之后
* 首先执行的是onProExecute方法
* 其次执行doInBackgroup方法
*
*/
public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {
private TextView textView;
private ProgressBar progressBar;
public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {
super();
this.textView = textView;
this.progressBar = progressBar;
}
/**
* 这里的Integer参数对应AsyncTask中的第一个参数
* 这里的String返回值对应AsyncTask的第三个参数
* 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
* 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
*/
@Override
protected String doInBackground(Integer... params) {
NetOperator netOperator = new NetOperator();
int i = 0;
for (i = 10; i <= 100; i+=10) {
netOperator.operator();
publishProgress(i);
}
return i + params[0].intValue() + "";
} /**
* 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值)
* 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置
*/
@Override
protected void onPostExecute(String result) {
textView.setText("异步操作执行结束" + result);
}
//该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置
@Override
protected void onPreExecute() {
textView.setText("开始执行异步线程");
}
/**
* 这里的Intege参数对应AsyncTask中的第二个参数
* 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行
* onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作
*/
@Override
protected void onProgressUpdate(Integer... values) {
int vlaue = values[0];
progressBar.setProgress(vlaue);
}
}

android UI线程安全问题的更多相关文章

  1. Android UI线程和非UI线程

    Android UI线程和非UI线程 UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这 ...

  2. 有关android UI 线程

    1. GUI线程框架 常见的 Swing, SWT框架都是作为单线程子系统来实现的,实际上不仅限于在Java中, Qt.MacOS Cocoa以及其他的环境中的GUI框架都是单线程的.虽然很多人尝试过 ...

  3. android脚步---如何看log之程序停止运行,和UI线程和非UI线程之间切换

    经常运行eclipse时,烧到手机出现,“停止运行”,这时候得通过logcat查log了.一般这种情况属于FATAL EXCEPTION,所以检索FATAL 或者 EXCEPTION,然后往下看几行 ...

  4. android 在非UI线程更新UI仍然成功原因深入剖析

    ”只能在UI主线程中更新View“. 这句话很熟悉吧? 来来,哥们,看一下下面的例子 @Override       protected void onCreate(Bundle savedInsta ...

  5. Android子线程真的不能更新UI么

    Android单线程模型是这样描述的: Android UI操作并不是线程安全的,并且这些操作必须在UI线程执行 如果在其它线程访问UI线程,Android提供了以下的方式: Activity.run ...

  6. Android子线程更新UI主线程方法之Handler

    背景: 我们开发应用程序的时候,处于线程安全的原因子线程通常是不能直接更新主线程(UI线程)中的UI元素的,那么在Android开发中有几种方法解决这个问题,其中方法之一就是利用Handler处理的. ...

  7. [原] Android performClick无效,UI线程理解

    原因 开发过程中遇到button.performClick()无效,原因是View.performClick()需要再UI线程中调用才会有效执行. 响应系统调用的方法(比如报告用户动作的onKeyDo ...

  8. 【Android开发学习笔记】【随笔】UI线程

    概念 UI线程 是Android中的主线程,涉及到UI方面的一些操作都需要在ui线程中进行操作 在非ui线程中想操作ui,就会报错 android.view.ViewRoot$CalledFromWr ...

  9. Android ActivityThread(主线程或UI线程)简介

    1. ActivityThread功能 它管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),并根据AMS的要求(通过IApplicationThread接口,AMS为Client ...

随机推荐

  1. 初探JavaScript魅力

    <style> #div1{width:200px; height:200px; background:red;} </style> </head> <scr ...

  2. CodeForces 682C Alyona and the Tree(广搜 + 技巧)

    方法:从根节点开始广搜,如果遇到了应该删除的点,就再广搜删掉它的子树并标记,然后统计一下被标记的个数就是答案,所谓技巧就是从根节点开始搜索的时候,如果遇到了某个节点的距离<0,就让它是0,0可以 ...

  3. 转:Loadrunner——Block(块)技术

    在使用LoadRunner时经常遇到这样一个问题,如果对不同的事务进行不同次数的循环该怎么处理?默认情况下LR对所有的事务都是统一执行的,即虽然有多个事务,但它们被执行的循环次数都是一样的,那么LR如 ...

  4. protobuf与json互相转换

    Java http://code.google.com/p/protobuf-java-format/ maven <dependency> <groupId>com.goog ...

  5. EtherChannel Cisco 端口聚合详解

    冗余连接及其实现 无论什么设备都无法保障运行的绝对稳定性,即使再优秀的产品也无法保证24×7不间断的工作.除去设备或模块损坏.传输线路中断等硬件故障原因以外,还可能由于网络流量过载.任务负荷过大而导致 ...

  6. iis8.0配置 使用备忘 403.14 - Forbidden Web 服务器被配置为不列出此目录的内容

    由于对iis的了解度不够,使用中总会碰到这样那样的问题,在这我先开个头,遇到问题再一一更新: 我用的是iis8; 1.发布到iis服务器下的网站你自己可以访问,局域网的其他机器不能? 答:是否开放了对 ...

  7. json转义字符串

    json前台写数据 @RequestMapping("/addUserJson") public void addUserJson(User user,HttpServletReq ...

  8. SqlServer拆分列

    SELECT TOP 1000 substring([a],0,CHARINDEX('/',a)) as low,substring([a],CHARINDEX('/',a)+1,len([a])-C ...

  9. Java——异常谜题

    1.谜题36  try和finally语句,代码如下,判断输出 public class Indecisive { public static void main(String[] args) { S ...

  10. Jedis使用示例

    http://javacrazyer.iteye.com/blog/1840161 http://www.cnblogs.com/edisonfeng/p/3571870.html