线程

  Android系统启动某个应用后,将会创建一个线程来运行该应用,这个线程成为“主”线程。主线程非常重要,这是因为它要负责消息的分发,给界面上相应的UI组件分发事件,包括绘图事件。这也是应用可以和UI组件(为android.widget和android.view中定义的组件)发生直接交互的线程。因此主线程也通常称为用户界面线程(UI线程)。

  Android系统不会主动为应用程序的组件创建额外的线程。运用在同一进程中所有程序组件都在UI线程中初始化,并使用UI线程来分发对这些程序组件的系统调用。由此可见,响应系统回调函数(比如onKeyDown() 响应用户按键或者某个生命周期回调函数)的方法总是使用UI线程来运行。

  比如,当用户触摸屏幕上某个按钮时,你的应用中的UI线程将把这个触摸事件发送到对应的UI小组件,然后该UI小组件设置其按下的状态并给事件队列发送一个刷新的请求,之后UI线程处理事件队列并通知该UI小组件重新绘制自身。

  当你的应用中响应用户事件时需要完成一些费事的工作时,这种单线程工作模式可能会导致非常差的用户响应性能。尤其是如果所有的工作都在UI线程中完成,比如访问网络,数据库查询等费时的工作将会阻塞UI线程。当UI线程被阻塞时,无法分发事件,包括绘图事件。此时从用户的角度来看,该应用看起来不再有响应。更为糟糕的是,如果UI线程阻塞超过几秒钟(目前为五秒),系统将给用户显示的“应用程序无响应”(ANR)对话框。用户可能会选择退出你的应用,更为甚者如果他们感觉很不满意也会选择卸载你的应用。

  此外,Android的UI组件包不是“线程安全”的,因此你不能走工作线程中调用UI组件的方法,所有有关UI的操作必须在UI线程中完成,因此下面为使用UI单线程工作线程的两个规则:

  1. 永远不要阻塞UI线程。

  2. 不要在非UI线程中操作UI组件。

  工作线程

  由于Android使用单线程工作模式,因此不阻塞UI线程对于应用程序的响应性能至关重要。如果在你的应用中包含一些不是一瞬间能完成的操作的话,你应用使用额外的线程(工作线程或是后台线程)来执行这些操作。

  比如下面示例,在用户点击某个按钮后,启动一个新线程来下载某个图像然后在ImageView中显示:

  [java] view plaincopyprint?

  public void onClick(View v) {

  new Thread(new Runnable() {

  public void run() {

  Bitmap b = loadImageFromNetwork("http://example.com/image.png");

  mImageView.setImageBitmap(b);

  }

  private Bitmap loadImageFromNetwork(String string) {

  // TODO Auto-generated method stub

  return null;

  }

  }).start();

  }

  public void onClick(View v) {

  new Thread(new Runnable() {

  public void run() {

  Bitmap b = loadImageFromNetwork("http://example.com/image.png");

  mImageView.setImageBitmap(b);

  }

  private Bitmap loadImageFromNetwork(String string) {

  // TODO Auto-generated method stub

  return null;

  }

  }).start();

  }

  乍一看,这段代码应该很好的完成工作,因为它创建了一个新线程来完成网络操作。然而它违法了上面说的第二个规则:不要在非UI线程中操作UI组件。在这段代码中的工作线程中而不是在UI线程中,直接修改ImageView,这将导致一些不可以预见的后果,常常导致发现此类错误捕捉异常困难和费时。