在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提供了几种从其他线程访问主线程的方式:
- Activity.runOnUiThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable, long)
- 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 }