Android 进程和线程详解(2)

发表于:2013-03-27来源:Csdn作者:引路蜂点击数: 标签:线程
Android系统不会主动为应用程序的组件创建额外的线程。运用在同一进程中所有程序组件都在UI线程中初始化,并使用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,这将导致一些不可以预见的后果,常常导致发现此类错误捕捉异常困难和费时。

  为了更正此类错误,Android提供了多种方法使得在非UI线程中访问UI组件,下面给出了其中的几种方法:

  · Activity.runOnUiThread (Runnable) 方法

  · View.post (Runnable) 方法

  · View.postDelayed (Runnable) 方法

  比如,使用View.post(Runnable)修改上面的代码:

  [java] view plaincopyprint?

  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();

  }

  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();

  }

  这样的实现是符合“线程安全”原则的:在额外的线程中完成网络操作并且在UI线程中完成对ImageView的操作。

  然而,随着操作复杂性的增加,上述代码可能会变得非常复杂导致维护困难。为了解决工作线程中处理此类复杂操作,你可能会考虑在工作线程中使用Handler类来处理由UI线程发送过来的消息。但可能使用AsyncTask是此类问题的最好解决方案,它很好的简化了工作线程需要和UI组件发生交互的问题。

  使用AsyncTask

  AsyncTask允许你完成一些和用户界面相关的异步工作。它在一个工作线程中完成一些阻塞工作任务然后在任务完成后通知UI线程,这些都不需要你自己来管理工作线程。

  你必须从AsyncTask派生一个子类并实现doInBackground()回调函数来使用AsyncTask,AsyncTask将使用后台进程池来执行异步任务。为了能够更新用户界面,你必须实现onPostExecute()方法,该方法将传递doInBackground()的返回结果,并且运行在UI线程中。然后你可以在UI线程中调用execute() 方法来执行该任务。

  比如,使用AsyncTask来完成之前的例子:

原文转自:http://blog.csdn.net/mapdigit/article/details/8711799