android UI线程安全问题

在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()方法。

下面写一个重载方法比较多得类:

 1 package vic.wong.main;
 2 import android.os.AsyncTask;
 3 import android.widget.ProgressBar;
 4 import android.widget.TextView;
 5
 6 /**
 7  * 生成该类的对象,并调用execute方法之后
 8  * 首先执行的是onProExecute方法
 9  * 其次执行doInBackgroup方法
10  *
11  */
12 public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {
13     private TextView textView;
14     private ProgressBar progressBar;
15     public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {
16         super();
17         this.textView = textView;
18         this.progressBar = progressBar;
19     }
20     /**
21      * 这里的Integer参数对应AsyncTask中的第一个参数
22      * 这里的String返回值对应AsyncTask的第三个参数
23      * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
24      * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
25      */
26     @Override
27     protected String doInBackground(Integer... params) {
28         NetOperator netOperator = new NetOperator();
29         int i = 0;
30         for (i = 10; i <= 100; i+=10) {
31             netOperator.operator();
32             publishProgress(i);
33         }
34         return i + params[0].intValue() + "";
35     }
36
37     /**
38      * 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值)
39      * 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置
40      */
41     @Override
42     protected void onPostExecute(String result) {
43         textView.setText("异步操作执行结束" + result);
44     }
45     //该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置
46     @Override
47     protected void onPreExecute() {
48         textView.setText("开始执行异步线程");
49     }
50     /**
51      * 这里的Intege参数对应AsyncTask中的第二个参数
52      * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行
53      * onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作
54      */
55     @Override
56     protected void onProgressUpdate(Integer... values) {
57         int vlaue = values[0];
58         progressBar.setProgress(vlaue);
59     }
60 }
时间: 2024-08-29 02:25:33

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

Android UI线程和非UI线程

UI线程及Android的单线程模型原则 当应用启动,系统会创建一个主线程(main thread). 这个主线程负责向UI组件分发事件(包括绘制事件),也是在这个主线程里,你的应用和Android的UI组件(components from the Android UI toolkit (components from the android.widget and android.view packages))发生交互. 所以main thread也叫UI thread也即UI线程. 系统不会为

Android - UI线程和异步(AsyncTask)线程

UI线程和异步(AsyncTask)线程 本文地址: http://blog.csdn.net/caroline_wendy Android的UI线程,也称作单线程,单线程应用串行运行所有命令,每个调用都是阻塞的. ANR(Application Not Responding)对话框,当应用程序一定时间(通常是5秒)无响应时,Android系统将会提示终止它. 主UI在前台运行,额外的UI(如联网)在后台运行:可以使用AsyncTask类,执行多线程. AsyncTask类主要有三个操作: do

Android UI线程安全

转载http://blog.csdn.net/dxpqxb/article/details/7987764 我们经常看见一句话:Android主线程是线程不安全的.这句话的意思是:更新UI只能是主线程的工作,子线程更新UI是线程不安全的,所以android里非主线程操作主UI就会报错.为什么呢?因为子线程可能会有多个,多个线程同时操作一个控件可能会有冲突发生,所以android就限定了只有主线程可以操作UI.子线程想操作UI,可以,你告诉我(主线程),我来更新. 在一个Android 程序开始运

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

经常运行eclipse时,烧到手机出现,“停止运行”,这时候得通过logcat查log了.一般这种情况属于FATAL EXCEPTION,所以检索FATAL 或者 EXCEPTION,然后往下看几行 例子: 11-26 16:18:17.949: E/AndroidRuntime(5363): FATAL EXCEPTION: Thread-19311-26 16:18:17.949: E/AndroidRuntime(5363): Process: com.scme.jiance, PID:

Android UI编程(4)——Thread、Message、Handler

当应用程序启动时,会开启一个主线程(也就是UI线程),由它来管理UI,监听用户点击,来响应用户并分发事件等.所有一般在主线程中不要执行比较耗时的操作,如延时.下载网络数据.死循环,否则出现ANR错误.所以就将这些操作放在子线程中,但是由于Android UI线程是不安全的,所有只能在主线程中更新UI.使用Thread来创建子线程.使用Message来存储数据.使用Handler来处理消息数据. 总结: 1.子线程与UI主线程之间通过Message来传递数据,需要创建一个新类(MyHandler)

Android自定义组件系列【12】——非UI线程绘图SurfaceView

一.SurfaceView的介绍 在前面我们已经会自定义View,使用canvas绘图,但是View的绘图机制存在一些缺陷. 1.View缺乏双缓冲机制. 2.程序必须重绘整个View上显示的图片,比较耗资源. 3.非UI线程无法更新View组件,所以会占用主线程资源,当需要在主线程中处理逻辑的时候会很慢. 在Android中为我们提供了一个SurfaceView来替代View实现绘制图形,一般在游戏绘图方面应用较广,所以如果是比较复杂的绘图建议使用SurfaceView. 二.SurfaceV

android中子线程更新UI的方式浅析

一.为何写作此文 ??你是不是经常看到很多书籍中说:不能在子线程中操作ui,不然会报错.你是不是也遇到了如下的疑惑(见下面的代码): @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); tv = (TextView) findViewById(R.id.tv); Threa

Android:子线程向UI主线程发送消息

在Android里,UI线程是不允许被阻塞的,因此我们要将耗时的工作放到子线程中去处理. 那么子线程耗时处理后要怎样通知UI线程呢? 我们可以在UI主线程中创建一个handler对象,然后通过重写其handleMessage(Message msg)的方法,该方法会接收到子线程中的handler对象的sendMessage((Message msg)发回来的消息.这样一发一收就完成工作: 而关于主线程向子线程发送消息的内容可以看我的上一篇博客,其中讲到了Looper类及其两个重要方法和实现原理.

Android 非UI线程中更新UI

Android 非UI线程中更新UI runOnUiThread(new Runnable() { public void run() { onDown(null); } });