默认情况下,一个应用进程中只会有一个线程,就是UI线程,或者说是main线程。当用户需要执行比较耗时的操作,如图片下载、更新数据库等等,如果都在UI线程中实现的话,会造成UI进程的阻塞,影响用户体验。因此,一般要另开辟一个线程,即worker线程,负责这些复杂的操作。如下面这段代码展示了如何在一个worker进程中下载图片并显示:
public void onClick(View v){
new Thread(newRunnable(){
public void run(){
Bitmap b = loadImageFromNetwork("http://example.com/image.png");
mImageView.setImageBitmap(b);
}
}).start();
}
这样就不会造成UI线程的阻塞了。但是,很不幸的是,这违背了UI线程使用准则之一:不要在UI线程之外获取Android UI tookit,这是因为Android UI tookit并不是线程安全的。
于是,上面例子需要将mImageView.setImageBitmap(b);放置在UI线程中执行。实现这个要求很简单,Android提供了好几种从worker线程中获取UI线程的方法:
• Activity.runOnUiThread(Runnable)
• View.post(Runnable)
• View.postDelayed(Runnable, long)
于是,只需要稍微修改一下代码:
public void onClick(View v) { new Thread(new Runnable() { public void run() { final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png"); mImageView.post(new Runnable() { public void run() { mImageView.setImageBitmap(bitmap); } }); } }).start(); }
除此以外,利用Handler也能很优雅地解决这个问题。但是,Android提供给我们一个更好的办法,就是使用AsyncTask类。
AsyncTask类
AsyncTask允许你将工作异步到用户界面中去。它将阻塞工作放到worker线程中,并将阻塞工作的结果利用UI线程同步到界面中,而不需要你亲自处理这些线程或者Handler的问题。
为了使用AsynTask,你需要设计它的一个子类,并实现doInBackground()回调方法,这个方法中的内容将会在worker线程中运行;然后,为了更新UI,你需要实现onPostExecute()方法,这个方法将会利用doInBackground()的返回结果,在UI线程中更新你的UI。
下面是解决加载网络图片的例子:
public void onClick(View v) { new DownloadImageTask().execute("http://example.com/image.png"); } private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> { /** The system calls this to perform work in a worker thread and * delivers it the parameters given to AsyncTask.execute() */ protected Bitmap doInBackground(String... urls) { return loadImageFromNetwork(urls[0]); } /** The system calls this to perform work in the UI thread and delivers * the result from doInBackground() */ protected void onPostExecute(Bitmap result) { mImageView.setImageBitmap(result); } }
这时候,通过这个简单的方法,你的线程就变成安全的了。