当 Android 应用运行的时候,系统会创建一个线程用于管理应用的界面和各个接口模块、组件。通常,这个线程也成为UI线程(UI thread)。同样,在Android中,所有的组件都工作在UI线程中,系统并不会为组件开辟单独的线程。因此,只有UI 线程才能够修改用户界面。
在 UI 线程中执行耗时操作会造成 UI 线程阻塞,从而导致应用程序对于用户的响应变慢,影响用户体验。因此,在设计中,需要避免耗时操作阻塞UI 线程。另一方面,Android UI 工具并非线程安全,因此,在 UI 线程外是禁止对 UI 进行访问和操作。
那么,当应用需要执行耗时操作的时候,怎么办?以下面的情况为例,应用程序希望在点击之后,在后台下载图片通过 ImageView 显示出来。这段程序符合不再 UI 线程中执行耗时操作的原则,但是,却在非 UI 线程中,对 UI 组件 ImageView 进行操作。这种操作会使程序运行出现异常。
public void onClick(View v) {可以考虑采用如下的一些方法来解决这个问题:
new Thread(new Runnable() {
public void run() {
Bitmap b = loadImageFromNetwork("http://example.com/image.png");
mImageView.setImageBitmap(b);
}
}).start();
}
- Activity.runOnUiThread(Runnable)
- View.post(Runnable)
- View.postDelayed(Runnable, long)
下面是运用 View.post(Runnable) 方法实现相同功能的代码示例:
public void onClick(View v) {同样,还可以采用异步任务 AsyncTask 的方式实现同样的功能,比如下面的实例。按钮按下后,执行异步任务,只需要完成相应的异步任务回调函数就好。
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();
}
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);
}
}