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 }
原文地址:https://www.cnblogs.com/chuanwei-zhang/p/4015353.html