首页 > 代码库 > android UI线程安全问题

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     @Override27     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     @Override42     protected void onPostExecute(String result) {43         textView.setText("异步操作执行结束" + result);44     }45     //该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI空间进行设置46     @Override47     protected void onPreExecute() {48         textView.setText("开始执行异步线程");49     }50     /**51      * 这里的Intege参数对应AsyncTask中的第二个参数52      * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行53      * onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作54      */55     @Override56     protected void onProgressUpdate(Integer... values) {57         int vlaue = values[0];58         progressBar.setProgress(vlaue);59     }60 }

 

android UI线程安全问题