在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. div里面的margin-top失效

    div标签中的元素margin-top失效的解决方法 元素上级标签是div,已经设置了width和height等的属性,可是,在对元素使用margin进行调整的时候,无法生效,下面有个不错的解决方法, ...

  2. byte 读写文件

    import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import ja ...

  3. 转:浏览器与WEB服务器工作过程举例

    用户通过“浏览器”访问因特网上的WEB服务器,浏览器和服务器之间的信息交换使用超文本传输协议(HTTP--HyperText Transfer Protocol). 例:用户访问东南大学主页 Http ...

  4. 未能加载文件或程序集 Microsoft.VisualBasic.PowerPacks.Vs, Version=10.0.0.0 解决 亲测

    项目打开winform程序做的某些窗体时报错: ************* 异常文本 ************** System.Reflection.TargetInvocationExceptio ...

  5. HTML的TextArea标记跟随文本内容自动设置高度

    js <textarea name="textarea" id="textarea" style='overflow-y: hidden;height:2 ...

  6. Entity Framework 学习初级篇2--ObjectContext、ObjectQuery、ObjectStateEntry、ObjectStateManager类的介绍

    本节,简单的介绍EF中的ObjectContext.ObjectQuery.ObjectStateEntry.ObjectStateManager这个几个比较重要的类,它们都位于System.Data ...

  7. 转 Android开发者指南-Manifest.xml-<supports-screens

    <supports-screens> 版本:Android 3.2 语法: <supports-screensandroid:resizeable=["true" ...

  8. java集合框架list和set

    为什么出现集合类? 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一中方式 数组和集合类同是容器,有何不同? 数组虽然也可以存储对象 ...

  9. Hibernate配置文件中配置各种数据库的driver、URL

    hibernate.properties ######################### Query Language ######################### ## define qu ...

  10. HDU 1203 I NEED A OFFER! 01背包 概率运算预处理。

    题目大意:中问题就不说了 ^—^~ 题目思路:从题目来看是很明显的01背包问题,被录取的概率记为v[],申请费用记为w[].但是我们可以预先做个处理,使问题解决起来更方便:v[]数组保留不被录取的概率 ...