在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. JDBC连接(MySql)数据库步骤,以及查询、插入、删除、更新等十一个处理数据库信息的功能

    主要内容:  JDBC连接数据库步骤. 一个简单详细的查询数据的例子. 封装连接数据库,释放数据库连接方法. 实现查询,插入,删除,更新等十一个处理数据库信息的功能.(包括事务处理,批量更新等) 把十 ...

  2. UVALive 7291 Kinfolk(最近公共祖先)

    题目中的描述就很最近公共祖先,再说其实这个题并不难,就是麻烦点(代码其实可以化简的),我写的判定比较多. 方法:求出两者的最近公共祖先lca,在求出两者到lca的距离 分析:给出a和b,如果LCA(a ...

  3. Python基础学习2---模块

    模块 你已经学习了如何在你的程序中定义一次函数而重用代码.如果你想要在其他程序中重用很多函数,那么你该如何编写程序呢?你可能已经猜到了,答案是使用模块.模块基本上就是一个包含了所有你定义的函数和变量的 ...

  4. CSS3简介

    选择器 盒模型 背景和边框 文字特效 2d/3d转换 动画 多列布局 用户界面

  5. ActiveXObject函数详解(转)

    什么是 ActiveX 控件? ActiveX 控件广泛用于 Internet.它们可以通过提供视频.动画内容等来增加浏览的乐趣.不过,这些程序可能出问题或者向您提供不需要的内容.在某些情况下,这些程 ...

  6. HDU 5215 BestCoder"杯中国大学生程序设计冠军赛” 边双连通分量取出子图+二分染色判图内奇偶环

    Cycle Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Sub ...

  7. Java回调函数的理解

    所谓回调,就是客户程序C调用服务程序S中的某个函数A,然后S又在某个时候反过来调用C中的某个函数B,对于C来说,这个B便叫做回调函数.例如Win32下的窗口过程函数就是一个典型的回调函数.一般说来,C ...

  8. 进程waitpid()的用法

    代码分析: /* waitpid.c */ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h ...

  9. 上传数据插件 Easy Populate 遇到问题 ?langer=remove

    批量上传,出现如下错误提示: ADD NEW PRODUCT FAILED! – Model: no200 – SQL error. Check Easy Populate error log in ...

  10. 两列布局,读《css那些事儿》

    两列布局: 1.两列定宽: 要点:float.width固定. :after清除浮动. 前提:两列的盒模型宽度相加不能大于父元素的宽度,否则会出现错位现象. <!DOCTYPE html> ...