Android应用程序线程消息循环模型分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6905587

我们知道,Android应用程序是通过消息来驱动的,即在应用程序的主线程(UI线程)中有一个消息循环,负责处理消息队列中的消息。我们也知 道,Android应用程序是支持多线程的,即可以创建子线程来执行一些计算型的任务,那么,这些子线程能不能像应用程序的主线程一样具有消息循环呢?这 些子线程又能不能往应用程序的主线程中发送消息呢?本文将分析Android应用程序线程消息处理模型,为读者解答这两个问题

在开发Android应用程序中,有时候我们需要在应用程序中创建一些常驻的子线程来不定期地执行一些不需要与应用程序界面交互的计算型的任务。如果这 些子线程具有消息循环,那么它们就能够常驻在应用程序中不定期的执行一些计算型任务了:当我们需要用这些子线程来执行任务时,就往这个子线程的消息队列中 发送一个消息,然后就可以在子线程的消息循环中执行我们的计算型任务了。我们在前面一篇文章Android系统默认Home应用程序(Launcher)的启动过程源代码分析中, 介绍Launcher的启动过程时,在Step 15(LauncherModel.startLoader)中,Launcher就是通过往一个子线程的消息队列中发送一个消息 (sWorker.post(mLoaderTask)),然后子线程就会在它的消息循环中处理这个消息的时候执行从 PackageManagerService中获取系统中已安装应用程序的信息列表的任务,即调用Step 16中的LoaderTask.run函数。

在开发Android应用程序中,有时候我们又需要在应用程序中创建一些子线程来执行一些需要与应用程序界面进交互的计算型任务。典型的应用场景是当我 们要从网上下载文件时,为了不使主线程被阻塞,我们通常创建一个子线程来负责下载任务,同时,在下载的过程,将下载进度以百分比的形式在应用程序的界面上 显示出来,这样就既不会阻塞主线程的运行,又能获得良好的用户体验。但是,我们知道,Android应用程序的子线程是不可以操作主线程的UI的,那么, 这个负责下载任务的子线程应该如何在应用程序界面上显示下载的进度呢?如果我们能够在子线程中往主线程的消息队列中发送消息,那么问题就迎刃而解了,因为 发往主线程消息队列的消息最终是由主线程来处理的,在处理这个消息的时候,我们就可以在应用程序界面上显示下载进度了。

上面提到的这两种情况,Android系统都为我们提供了完善的解决方案,前者可以通过使用HandlerThread类来实现,而后者可以使用 AsyncTask类来实现,本文就详细这两个类是如何实现的。不过,为了更好地理解HandlerThread类和AsyncTask类的实现,我们先 来看看应用程序的主线程的消息循环模型是如何实现的。

1. 应用程序主线程消息循环模型

在前面一篇文章Android应用程序进程启动过程的源代码分析一 文中,我们已经分析应用程序进程(主线程)的启动过程了,这里主要是针对它的消息循环模型作一个总结。当运行在Android应用程序框架层中的 ActivityManagerService决定要为当前启动的应用程序创建一个主线程的时候,它会在ActivityManagerService中 的startProcessLocked成员函数调用Process类的静态成员函数start为当前应用程序创建一个主线程:

[java] view plaincopy

  1. public final class ActivityManagerService extends ActivityManagerNative
  2. implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
  3. ......
  4. private final void startProcessLocked(ProcessRecord app,
  5. String hostingType, String hostingNameStr) {
  6. ......
  7. try {
  8. int uid = app.info.uid;
  9. int[] gids = null;
  10. try {
  11. gids = mContext.getPackageManager().getPackageGids(
  12. app.info.packageName);
  13. } catch (PackageManager.NameNotFoundException e) {
  14. ......
  15. }
  16. ......
  17. int debugFlags = 0;
  18. ......
  19. int pid = Process.start("android.app.ActivityThread",
  20. mSimpleProcessManagement ? app.processName : null, uid, uid,
  21. gids, debugFlags, null);
  22. ......
  23. } catch (RuntimeException e) {
  24. ......
  25. }
  26. }
  27. ......
  28. }

这里我们主要关注Process.start函数的第一个参数“android.app.ActivityThread”,它表示要在当前新建的线程中 加载android.app.ActivityThread类,并且调用这个类的静态成员函数main作为应用程序的入口点。 ActivityThread类定义在frameworks/base/core/java/android/app /ActivityThread.java文件中:

[java] view plaincopy

  1. public final class ActivityThread {
  2. ......
  3. public static final void main(String[] args) {
  4. ......
  5. Looper.prepareMainLooper();
  6. ......
  7. ActivityThread thread = new ActivityThread();
  8. thread.attach(false);
  9. ......
  10. Looper.loop();
  11. ......
  12. thread.detach();
  13. ......
  14. }
  15. ......
  16. }

在这个main函数里面,除了创建一个ActivityThread实例外,就是在进行消息循环了。

在进行消息循环之前,首先会通过Looper类的静态成员函数prepareMainLooper为当前线程准备一个消息循环对象。Looper类定义 在frameworks/base/core/java/android/os/Looper.java文件中:

[java] view plaincopy

  1. public class Looper {
  2. ......
  3. // sThreadLocal.get() will return null unless you‘ve called prepare().
  4. private static final ThreadLocal sThreadLocal = new ThreadLocal();
  5. ......
  6. private static Looper mMainLooper = null;
  7. ......
  8. public static final void prepare() {
  9. if (sThreadLocal.get() != null) {
  10. throw new RuntimeException("Only one Looper may be created per thread");
  11. }
  12. sThreadLocal.set(new Looper());
  13. }
  14. ......
  15. public static final void prepareMainLooper() {
  16. prepare();
  17. setMainLooper(myLooper());
  18. ......
  19. }
  20. private synchronized static void setMainLooper(Looper looper) {
  21. mMainLooper = looper;
  22. }
  23. public synchronized static final Looper getMainLooper() {
  24. return mMainLooper;
  25. }
  26. ......
  27. public static final Looper myLooper() {
  28. return (Looper)sThreadLocal.get();
  29. }
  30. ......
  31. }

Looper类的静态成员函数prepareMainLooper是专门应用程序的主线程调用的,应用程序的其它子线程都不应该调用这个函数来在本线程 中创建消息循环对象,而应该调用prepare函数来在本线程中创建消息循环对象,下一节我们介绍一个线程类HandlerThread 时将会看到。

为什么要为应用程序的主线程专门准备一个创建消息循环对象的函数呢?这是为了让其它地方能够方便地通过Looper类的getMainLooper函数 来获得应用程序主线程中的消息循环对象。获得应用程序主线程中的消息循环对象又有什么用呢?一般就是为了能够向应用程序主线程发送消息了。

在prepareMainLooper函数中,首先会调用prepare函数在本线程中创建一个消息循环对象,然后将这个消息循环对象放在线程局部变量sThreadLocal中:

[java] view plaincopy

  1. sThreadLocal.set(new Looper());

接着再将这个消息循环对象通过调用setMainLooper函数来保存在Looper类的静态成员变量mMainLooper中:

[java] view plaincopy

  1. mMainLooper = looper;

这样,其它地方才可以调用getMainLooper函数来获得应用程序主线程中的消息循环对象。

消息循环对象创建好之后,回到ActivityThread类的main函数中,接下来,就是要进入消息循环了:

[java] view plaincopy

  1. Looper.loop();

Looper类具体是如何通过loop函数进入消息循环以及处理消息队列中的消息,可以参考前面一篇文章Android应用程序消息处理机制(Looper、Handler)分析,这里就不再分析了,我们只要知道ActivityThread类中的main函数执行了这一步之后,就为应用程序的主线程准备好消息循环就可以了。

2. 应用程序子线程消息循环模型

在Java框架中,如果我们想在当前应用程序中创建一个子线程,一般就是通过自己实现一个类,这个类继承于Thread类,然后重载Thread类的 run函数,把我们想要在这个子线程执行的任务都放在这个run函数里面实现。最后实例这个自定义的类,并且调用它的start函数,这样一个子线程就创 建好了,并且会调用这个自定义类的run函数。但是当这个run函数执行完成后,子线程也就结束了,它没有消息循环的概念。

前面说过,有时候我们需要在应用程序中创建一些常驻的子线程来不定期地执行一些计算型任务,这时候就可以考虑使用Android系统提供的HandlerThread类了,它具有创建具有消息循环功能的子线程的作用。

HandlerThread类实现在frameworks/base/core/java/android/os/HandlerThread.java文件中,这里我们通过使用情景来有重点的分析它的实现。

在前面一篇文章Android系统默认Home应用程序(Launcher)的启动过程源代码分析中, 我们分析了Launcher的启动过程,其中在Step 15(LauncherModel.startLoader)和Step 16(LoaderTask.run)中,Launcher会通过创建一个HandlerThread类来实现在一个子线程加载系统中已经安装的应用程序 的任务:

[java] view plaincopy

  1. public class LauncherModel extends BroadcastReceiver {
  2. ......
  3. private LoaderTask mLoaderTask;
  4. private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
  5. static {
  6. sWorkerThread.start();
  7. }
  8. private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
  9. ......
  10. public void startLoader(Context context, boolean isLaunching) {
  11. ......
  12. synchronized (mLock) {
  13. ......
  14. // Don‘t bother to start the thread if we know it‘s not going to do anything
  15. if (mCallbacks != null && mCallbacks.get() != null) {
  16. ......
  17. mLoaderTask = new LoaderTask(context, isLaunching);
  18. sWorker.post(mLoaderTask);
  19. }
  20. }
  21. }
  22. ......
  23. private class LoaderTask implements Runnable {
  24. ......
  25. public void run() {
  26. ......
  27. keep_running: {
  28. ......
  29. // second step
  30. if (loadWorkspaceFirst) {
  31. ......
  32. loadAndBindAllApps();
  33. } else {
  34. ......
  35. }
  36. ......
  37. }
  38. ......
  39. }
  40. ......
  41. }
  42. ......
  43. }

在这个LauncherModel类中,首先创建了一个HandlerThread对象:

[java] view plaincopy

  1. private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");

接着调用它的start成员函数来启动一个子线程:

[java] view plaincopy

  1. static {
  2. sWorkerThread.start();
  3. }

接着还通过这个HandlerThread对象的getLooper函数来获得这个子线程中的消息循环对象,并且使用这个消息循环创建对象来创建一个Handler:

[java] view plaincopy

  1. private static final Handler sWorker = new Handler(sWorkerThread.getLooper());

有了这个Handler对象sWorker之后,我们就可以往这个子线程中发送消息,然后在处理这个消息的时候执行加载系统中已经安装的应用程序的任务了,在startLoader函数中:

[java] view plaincopy

  1. mLoaderTask = new LoaderTask(context, isLaunching);
  2. sWorker.post(mLoaderTask);

这里的mLoaderTask是一个LoaderTask对象,它实现了Runnable接口,因此,可以把这个LoaderTask对象作为参数传给 sWorker.post函数。在sWorker.post函数里面,会把这个LoaderTask对象封装成一个消息,并且放入这个子线程的消息队列中 去。当这个子线程的消息循环处理这个消息的时候,就会调用这个LoaderTask对象的run函数,因此,我们就可以在LoaderTask对象的 run函数中通过调用loadAndBindAllApps来执行加载系统中已经安装的应用程序的任务了。

了解了HanderThread类的使用方法之后,我们就可以重点地来分析它的实现了:

[java] view plaincopy

  1. public class HandlerThread extends Thread {
  2. ......
  3. private Looper mLooper;
  4. public HandlerThread(String name) {
  5. super(name);
  6. ......
  7. }
  8. ......
  9. public void run() {
  10. ......
  11. Looper.prepare();
  12. synchronized (this) {
  13. mLooper = Looper.myLooper();
  14. ......
  15. }
  16. ......
  17. Looper.loop();
  18. ......
  19. }
  20. public Looper getLooper() {
  21. ......
  22. return mLooper;
  23. }
  24. ......
  25. }

首先我们看到的是,HandlerThread类继承了Thread类,因此,通过它可以在应用程序中创建一个子线程,其次我们看到在它的run函数中,会进入一个消息循环中,因此,这个子线程可以常驻在应用程序中,直到它接收收到一个退出消息为止。

在run函数中,首先是调用Looper类的静态成员函数prepare来准备一个消息循环对象:

[java] view plaincopy

  1. Looper.prepare();

然后通过Looper类的myLooper成员函数将这个子线程中的消息循环对象保存在HandlerThread类中的成员变量mLooper中:

[java] view plaincopy

  1. mLooper = Looper.myLooper();

这样,其它地方就可以方便地通过它的getLooper函数来获得这个消息循环对象了,有了这个消息循环对象后,就可以往这个子线程的消息队列中发送消息,通知这个子线程执行特定的任务了。

最在这个run函数通过Looper类的loop函数进入消息循环中:

[java] view plaincopy

  1. Looper.loop();

这样,一个具有消息循环的应用程序子线程就准备就绪了。

HandlerThread类的实现虽然非常简单,当然这得益于Java提供的Thread类和Android自己本身提供的Looper类,但是它的想法却非常周到,为应用程序开发人员提供了很大的方便。
        3. 需要与UI交互的应用程序子线程消息模型

前面说过,我们开发应用程序的时候,经常中需要创建一个子线程来在后台执行一个特定的计算任务,而在这个任务计算的过程中,需要不断地将计算进度或者计
算结果展现在应用程序的界面中。典型的例子是从网上下载文件,为了不阻塞应用程序的主线程,我们开辟一个子线程来执行下载任务,子线程在下载的同时不断地
将下载进度在应用程序界面上显示出来,这样做出来程序就非常友好。由于子线程不能直接操作应用程序的UI,因此,这时候,我们就可以通过往应用程序的主线
程中发送消息来通知应用程序主线程更新界面上的下载进度。因为类似的这种情景在实际开发中经常碰到,Android系统为开发人员提供了一个异步任务类
(AsyncTask)来实现上面所说的功能,即它会在一个子线程中执行计算任务,同时通过主线程的消息循环来获得更新应用程序界面的机会。

为了更好地分析AsyncTask的实现,我们先举一个例子来说明它的用法。在前面一篇文章Android系统中的广播(Broadcast)机制简要介绍和学习计划中,
我们开发了一个应用程序Broadcast,其中使用了AsyncTask来在一个线程在后台在执行计数任务,计数过程通过广播(Broadcast)来
将中间结果在应用程序界面上显示出来。在这个例子中,使用广播来在应用程序主线程和子线程中传递数据不是最优的方法,当时只是为了分析Android系统
的广播机制而有意为之的。在本节内容中,我们稍微这个例子作一个简单的修改,就可以通过消息的方式来将计数过程的中间结果在应用程序界面上显示出来。

为了区别Android系统中的广播(Broadcast)机制简要介绍和学习计划
文中使用的应用程序Broadcast,我们将本节中使用的应用程序命名为Counter。首先在Android源代码工程中创建一个Android应用
程序工程,名字就为Counter,放在packages/experimental目录下。关于如何获得Android源代码工程,请参考在Ubuntu上下载、编译和安装Android最新源代码一文;关于如何在Android源代码工程中创建应用程序工程,请参考在Ubuntu上为Android系统内置Java应用程序测试Application Frameworks层的硬件服务一文。这个应用程序工程定义了一个名为shy.luo.counter的package,这个例子的源代码主要就是实现在这个目录下的Counter.java文件中:

[java] view plaincopy

  1. package shy.luo.counter;
  2. import android.app.Activity;
  3. import android.content.ComponentName;
  4. import android.content.Context;
  5. import android.content.Intent;
  6. import android.content.IntentFilter;
  7. import android.os.Bundle;
  8. import android.os.AsyncTask;
  9. import android.util.Log;
  10. import android.view.View;
  11. import android.view.View.OnClickListener;
  12. import android.widget.Button;
  13. import android.widget.TextView;
  14. public class Counter extends Activity implements OnClickListener {
  15. private final static String LOG_TAG = "shy.luo.counter.Counter";
  16. private Button startButton = null;
  17. private Button stopButton = null;
  18. private TextView counterText = null;
  19. private AsyncTask<Integer, Integer, Integer> task = null;
  20. private boolean stop = false;
  21. @Override
  22. public void onCreate(Bundle savedInstanceState) {
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.main);
  25. startButton = (Button)findViewById(R.id.button_start);
  26. stopButton = (Button)findViewById(R.id.button_stop);
  27. counterText = (TextView)findViewById(R.id.textview_counter);
  28. startButton.setOnClickListener(this);
  29. stopButton.setOnClickListener(this);
  30. startButton.setEnabled(true);
  31. stopButton.setEnabled(false);
  32. Log.i(LOG_TAG, "Main Activity Created.");
  33. }
  34. @Override
  35. public void onClick(View v) {
  36. if(v.equals(startButton)) {
  37. if(task == null) {
  38. task = new CounterTask();
  39. task.execute(0);
  40. startButton.setEnabled(false);
  41. stopButton.setEnabled(true);
  42. }
  43. } else if(v.equals(stopButton)) {
  44. if(task != null) {
  45. stop = true;
  46. task = null;
  47. startButton.setEnabled(true);
  48. stopButton.setEnabled(false);
  49. }
  50. }
  51. }
  52. class CounterTask extends AsyncTask<Integer, Integer, Integer> {
  53. @Override
  54. protected Integer doInBackground(Integer... vals) {
  55. Integer initCounter = vals[0];
  56. stop = false;
  57. while(!stop) {
  58. publishProgress(initCounter);
  59. try {
  60. Thread.sleep(1000);
  61. } catch (InterruptedException e) {
  62. e.printStackTrace();
  63. }
  64. initCounter++;
  65. }
  66. return initCounter;
  67. }
  68. @Override
  69. protected void onProgressUpdate(Integer... values) {
  70. super.onProgressUpdate(values);
  71. String text = values[0].toString();
  72. counterText.setText(text);
  73. }
  74. @Override
  75. protected void onPostExecute(Integer val) {
  76. String text = val.toString();
  77. counterText.setText(text);
  78. }
  79. };
  80. }

这个计数器程序很简单,它在界面上有两个按钮Start和Stop。点击Start按钮时,便会创建一个CounterTask实例task,然后调用
它的execute函数就可以在应用程序中启动一个子线程,并且通过调用这个CounterTask类的doInBackground函数来执行计数任
务。在计数的过程中,会通过调用publishProgress函数来将中间结果传递到onProgressUpdate函数中去,在
onProgressUpdate函数中,就可以把中间结果显示在应用程序界面了。点击Stop按钮时,便会通过设置变量stop为true,这
样,CounterTask类的doInBackground函数便会退出循环,然后将结果返回到onPostExecute函数中去,在
onPostExecute函数,会把最终计数结果显示在用程序界面中。

在这个例子中,我们需要注意的是:

A. CounterTask类继承于AsyncTask类,因此它也是一个异步任务类;

B. CounterTask类的doInBackground函数是在后台的子线程中运行的,这时候它不可以操作应用程序的界面;

C. CounterTask类的onProgressUpdate和onPostExecute两个函数是应用程序的主线程中执行,它们可以操作应用程序的界面。

关于C这一点的实现原理,我们在后面会分析到,这里我们先完整地介绍这个例子,以便读者可以参考做一下实验。

接下来我们再看看应用程序的配置文件AndroidManifest.xml:

[html] view plaincopy

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="shy.luo.counter"
  4. android:versionCode="1"
  5. android:versionName="1.0">
  6. <application android:icon="@drawable/icon" android:label="@string/app_name">
  7. <activity android:name=".Counter"
  8. android:label="@string/app_name">
  9. <intent-filter>
  10. <action android:name="android.intent.action.MAIN" />
  11. <category android:name="android.intent.category.LAUNCHER" />
  12. </intent-filter>
  13. </activity>
  14. </application>
  15. </manifest>

这个配置文件很简单,我们就不介绍了。

再来看应用程序的界面文件,它定义在res/layout/main.xml文件中:

[html] view plaincopy

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:orientation="vertical"
  4. android:layout_width="fill_parent"
  5. android:layout_height="fill_parent"
  6. android:gravity="center">
  7. <LinearLayout
  8. android:layout_width="fill_parent"
  9. android:layout_height="wrap_content"
  10. android:layout_marginBottom="10px"
  11. android:orientation="horizontal"
  12. android:gravity="center">
  13. <TextView
  14. android:layout_width="wrap_content"
  15. android:layout_height="wrap_content"
  16. android:layout_marginRight="4px"
  17. android:gravity="center"
  18. android:text="@string/counter">
  19. </TextView>
  20. <TextView
  21. android:id="@+id/textview_counter"
  22. android:layout_width="wrap_content"
  23. android:layout_height="wrap_content"
  24. android:gravity="center"
  25. android:text="0">
  26. </TextView>
  27. </LinearLayout>
  28. <LinearLayout
  29. android:layout_width="fill_parent"
  30. android:layout_height="wrap_content"
  31. android:orientation="horizontal"
  32. android:gravity="center">
  33. <Button
  34. android:id="@+id/button_start"
  35. android:layout_width="wrap_content"
  36. android:layout_height="wrap_content"
  37. android:gravity="center"
  38. android:text="@string/start">
  39. </Button>
  40. <Button
  41. android:id="@+id/button_stop"
  42. android:layout_width="wrap_content"
  43. android:layout_height="wrap_content"
  44. android:gravity="center"
  45. android:text="@string/stop" >
  46. </Button>
  47. </LinearLayout>
  48. </LinearLayout>

这个界面配置文件也很简单,等一下我们在模拟器把这个应用程序启动起来后,就可以看到它的截图了。

应用程序用到的字符串资源文件位于res/values/strings.xml文件中:

[html] view plaincopy

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3. <string name="app_name">Counter</string>
  4. <string name="counter">Counter: </string>
  5. <string name="start">Start Counter</string>
  6. <string name="stop">Stop Counter</string>
  7. </resources>

最后,我们还要在工程目录下放置一个编译脚本文件Android.mk:

[html] view plaincopy

  1. LOCAL_PATH:= $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_MODULE_TAGS := optional
  4. LOCAL_SRC_FILES := $(call all-subdir-java-files)
  5. LOCAL_PACKAGE_NAME := Counter
  6. include $(BUILD_PACKAGE)

接下来就要编译了。有关如何单独编译Android源代码工程的模块,以及如何打包system.img,请参考如何单独编译Android源代码中的模块一文。
       执行以下命令进行编译和打包:

[html] view plaincopy

  1. [email protected]:~/Android$ mmm packages/experimental/Counter
  2. [email protected]:~/Android$ make snod

这样,打包好的Android系统镜像文件system.img就包含我们前面创建的Counter应用程序了。
       再接下来,就是运行模拟器来运行我们的例子了。关于如何在Android源代码工程中运行模拟器,请参考在Ubuntu上下载、编译和安装Android最新源代码一文。
       执行以下命令启动模拟器:

[html] view plaincopy

  1. [email protected]:~/Android$ emulator

最后我们就可以在Launcher中找到Counter应用程序图标,把它启动起来,点击Start按钮,就会看到应用程序界面上的计数器跑起来了:

这样,使用AsyncTask的例子就介绍完了,下面,我们就要根据上面对AsyncTask的使用情况来重点分析它的实现了。

AsyncTask类定义在frameworks/base/core/java/android/os/AsyncTask.java文件中:

[java] view plaincopy

  1. public abstract class AsyncTask<Params, Progress, Result> {
  2. ......
  3. private static final BlockingQueue<Runnable> sWorkQueue =
  4. new LinkedBlockingQueue<Runnable>(10);
  5. private static final ThreadFactory sThreadFactory = new ThreadFactory() {
  6. private final AtomicInteger mCount = new AtomicInteger(1);
  7. public Thread newThread(Runnable r) {
  8. return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
  9. }
  10. };
  11. ......
  12. private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
  13. MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
  14. private static final int MESSAGE_POST_RESULT = 0x1;
  15. private static final int MESSAGE_POST_PROGRESS = 0x2;
  16. private static final int MESSAGE_POST_CANCEL = 0x3;
  17. private static final InternalHandler sHandler = new InternalHandler();
  18. private final WorkerRunnable<Params, Result> mWorker;
  19. private final FutureTask<Result> mFuture;
  20. ......
  21. public AsyncTask() {
  22. mWorker = new WorkerRunnable<Params, Result>() {
  23. public Result call() throws Exception {
  24. ......
  25. return doInBackground(mParams);
  26. }
  27. };
  28. mFuture = new FutureTask<Result>(mWorker) {
  29. @Override
  30. protected void done() {
  31. Message message;
  32. Result result = null;
  33. try {
  34. result = get();
  35. } catch (InterruptedException e) {
  36. android.util.Log.w(LOG_TAG, e);
  37. } catch (ExecutionException e) {
  38. throw new RuntimeException("An error occured while executing doInBackground()",
  39. e.getCause());
  40. } catch (CancellationException e) {
  41. message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
  42. new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
  43. message.sendToTarget();
  44. return;
  45. } catch (Throwable t) {
  46. throw new RuntimeException("An error occured while executing "
  47. + "doInBackground()", t);
  48. }
  49. message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
  50. new AsyncTaskResult<Result>(AsyncTask.this, result));
  51. message.sendToTarget();
  52. }
  53. };
  54. }
  55. ......
  56. public final Result get() throws InterruptedException, ExecutionException {
  57. return mFuture.get();
  58. }
  59. ......
  60. public final AsyncTask<Params, Progress, Result> execute(Params... params) {
  61. ......
  62. mWorker.mParams = params;
  63. sExecutor.execute(mFuture);
  64. return this;
  65. }
  66. ......
  67. protected final void publishProgress(Progress... values) {
  68. sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
  69. new AsyncTaskResult<Progress>(this, values)).sendToTarget();
  70. }
  71. private void finish(Result result) {
  72. ......
  73. onPostExecute(result);
  74. ......
  75. }
  76. ......
  77. private static class InternalHandler extends Handler {
  78. @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
  79. @Override
  80. public void handleMessage(Message msg) {
  81. AsyncTaskResult result = (AsyncTaskResult) msg.obj;
  82. switch (msg.what) {
  83. case MESSAGE_POST_RESULT:
  84. // There is only one result
  85. result.mTask.finish(result.mData[0]);
  86. break;
  87. case MESSAGE_POST_PROGRESS:
  88. result.mTask.onProgressUpdate(result.mData);
  89. break;
  90. case MESSAGE_POST_CANCEL:
  91. result.mTask.onCancelled();
  92. break;
  93. }
  94. }
  95. }
  96. private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
  97. Params[] mParams;
  98. }
  99. private static class AsyncTaskResult<Data> {
  100. final AsyncTask mTask;
  101. final Data[] mData;
  102. AsyncTaskResult(AsyncTask task, Data... data) {
  103. mTask = task;
  104. mData = data;
  105. }
  106. }
  107. }

从AsyncTask的实现可以看出,当我们第一次创建一个AsyncTask对象时,首先会执行下面静态初始化代码创建一个线程池sExecutor:

[java] view plaincopy

  1. private static final BlockingQueue<Runnable> sWorkQueue =
  2. new LinkedBlockingQueue<Runnable>(10);
  3. private static final ThreadFactory sThreadFactory = new ThreadFactory() {
  4. private final AtomicInteger mCount = new AtomicInteger(1);
  5. public Thread newThread(Runnable r) {
  6. return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
  7. }
  8. };
  9. ......
  10. private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
  11. MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

这里的ThreadPoolExecutor是Java提供的多线程机制之一,这里用的构造函数原型为:

[java] view plaincopy

  1. ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
  2. BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)

各个参数的意义如下:

corePoolSize -- 线程池的核心线程数量

        maximumPoolSize -- 线程池的最大线程数量

        keepAliveTime -- 若线程池的线程数数量大于核心线程数量,那么空闲时间超过keepAliveTime的线程将被回收

        unit -- 参数keepAliveTime使用的时间单位

        workerQueue -- 工作任务队列

        threadFactory -- 用来创建线程池中的线程
  
   
 简单来说,ThreadPoolExecutor的运行机制是这样的:每一个工作任务用一个Runnable对象来表示,当我们要把一个工作任务交给这
个线程池来执行的时候,就通过调用ThreadPoolExecutor的execute函数来把这个工作任务加入到线程池中去。此时,如果线程池中的线
程数量小于corePoolSize,那么就会调用threadFactory接口来创建一个新的线程并且加入到线程池中去,再执行这个工作任务;如果线
程池中的线程数量等于corePoolSize,但是工作任务队列workerQueue未满,则把这个工作任务加入到工作任务队列中去等待执行;如果线
程池中的线程数量大于corePoolSize,但是小于maximumPoolSize,并且工作任务队列workerQueue已经满了,那么就会调
用threadFactory接口来创建一个新的线程并且加入到线程池中去,再执行这个工作任务;如果线程池中的线程量已经等于
maximumPoolSize了,并且工作任务队列workerQueue也已经满了,这个工作任务就被拒绝执行了。

创建好了线程池后,再创建一个消息处理器:

[java] view plaincopy

  1. private static final InternalHandler sHandler = new InternalHandler();

注意,这行代码是在应用程序的主线程中执行的,因此,这个消息处理器sHandler内部引用的消息循环对象looper是应用程序主线程的消息循环对象,消息处理器的实现机制具体可以参考前面一篇文章Android应用程序消息处理机制(Looper、Handler)分析

AsyncTask类的静态初始化代码执行完成之后,才开始创建AsyncTask对象,即执行AsyncTask类的构造函数:

[java] view plaincopy

  1. public AsyncTask() {
  2. mWorker = new WorkerRunnable<Params, Result>() {
  3. public Result call() throws Exception {
  4. ......
  5. return doInBackground(mParams);
  6. }
  7. };
  8. mFuture = new FutureTask<Result>(mWorker) {
  9. @Override
  10. protected void done() {
  11. Message message;
  12. Result result = null;
  13. try {
  14. result = get();
  15. } catch (InterruptedException e) {
  16. android.util.Log.w(LOG_TAG, e);
  17. } catch (ExecutionException e) {
  18. throw new RuntimeException("An error occured while executing doInBackground()",
  19. e.getCause());
  20. } catch (CancellationException e) {
  21. message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
  22. new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
  23. message.sendToTarget();
  24. return;
  25. } catch (Throwable t) {
  26. throw new RuntimeException("An error occured while executing "
  27. + "doInBackground()", t);
  28. }
  29. message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
  30. new AsyncTaskResult<Result>(AsyncTask.this, result));
  31. message.sendToTarget();
  32. }
  33. };
  34. }

在AsyncTask类的构造函数里面,主要是创建了两个对象,分别是一个WorkerRunnable对象mWorker和一个FutureTask对象mFuture。

WorkerRunnable类实现了Callable接口,此外,它的内部成员变量mParams用于保存从AsyncTask对象的execute函数传进来的参数列表:

[java] view plaincopy

  1. private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
  2. Params[] mParams;
  3. }

FutureTask类也实现了Runnable接口,所以它可以作为一个工作任务通过调用AsyncTask类的execute函数添加到sExecuto线程池中去:

[java] view plaincopy

  1. public final AsyncTask<Params, Progress, Result> execute(Params... params) {
  2. ......
  3. mWorker.mParams = params;
  4. sExecutor.execute(mFuture);
  5. return this;
  6. }

这里的FutureTask对象mFuture是用来封装前面的WorkerRunnable对象mWorker。当mFuture加入到线程池中执行时,它调用的是mWorker对象的call函数:

[java] view plaincopy

  1. mWorker = new WorkerRunnable<Params, Result>() {
  2. public Result call() throws Exception {
  3. ......
  4. return doInBackground(mParams);
  5. }
  6. };

在call函数里面,会调用AsyncTask类的doInBackground函数来执行真正的任务,这个函数是要由AsyncTask的子类来实现的,注意,这个函数是在应用程序的子线程中执行的,它不可以操作应用程序的界面。

我们可以通过mFuture对象来操作当前执行的任务,例如查询当前任务的状态,它是正在执行中,还是完成了,还是被取消了,如果是完成了,还可以通过它获得任务的执行结果,如果还没有完成,可以取消任务的执行。

当工作任务mWorker执行完成的时候,mFuture对象中的done函数就会被被调用,根据任务的完成状况,执行相应的操作,例如,如果是因为异
常而完成时,就会抛异常,如果是正常完成,就会把任务执行结果封装成一个AsyncTaskResult对象:

[java] view plaincopy

  1. private static class AsyncTaskResult<Data> {
  2. final AsyncTask mTask;
  3. final Data[] mData;
  4. AsyncTaskResult(AsyncTask task, Data... data) {
  5. mTask = task;
  6. mData = data;
  7. }
  8. }

其中,成员变量mData保存的是任务执行结果,而成员变量mTask指向前面我们创建的AsyncTask对象。
        最后把这个AsyncTaskResult对象封装成一个消息,并且通过消息处理器sHandler加入到应用程序主线程的消息队列中:

[java] view plaincopy

  1. message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
  2. new AsyncTaskResult<Result>(AsyncTask.this, result));
  3. message.sendToTarget();

这个消息最终就会在InternalHandler类的handleMessage函数中处理了:

[java] view plaincopy

  1. private static class InternalHandler extends Handler {
  2. @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
  3. @Override
  4. public void handleMessage(Message msg) {
  5. AsyncTaskResult result = (AsyncTaskResult) msg.obj;
  6. switch (msg.what) {
  7. case MESSAGE_POST_RESULT:
  8. // There is only one result
  9. result.mTask.finish(result.mData[0]);
  10. break;
  11. ......
  12. }
  13. }
  14. }

在这个函数里面,最终会调用前面创建的这个AsyncTask对象的finish函数来进一步处理:

[java] view plaincopy

  1. private void finish(Result result) {
  2. ......
  3. onPostExecute(result);
  4. ......
  5. }

这个函数调用AsyncTask类的onPostExecute函数来进一步处理,AsyncTask类的onPostExecute函数一般是要由其子类来重载的,注意,这个函数是在应用程序的主线程中执行的,因此,它可以操作应用程序的界面。
        在任务执行的过程当中,即执行doInBackground函数时候,可能通过调用publishProgress函数来将中间结果封装成一个消息发送到应用程序主线程中的消息队列中去:

[java] view plaincopy

  1. protected final void publishProgress(Progress... values) {
  2. sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
  3. new AsyncTaskResult<Progress>(this, values)).sendToTarget();
  4. }

这个消息最终也是由InternalHandler类的handleMessage函数来处理的:

[java] view plaincopy

  1. private static class InternalHandler extends Handler {
  2. @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
  3. @Override
  4. public void handleMessage(Message msg) {
  5. AsyncTaskResult result = (AsyncTaskResult) msg.obj;
  6. switch (msg.what) {
  7. ......
  8. case MESSAGE_POST_PROGRESS:
  9. result.mTask.onProgressUpdate(result.mData);
  10. break;
  11. ......
  12. }
  13. }
  14. }

这里它调用前面创建的AsyncTask对象的onPorgressUpdate函数来进一步处理,这个函数一般是由AsyncTask的子类来实现
的,注意,这个函数是在应用程序的主线程中执行的,因此,它和前面的onPostExecute函数一样,可以操作应用程序的界面。

这样,AsyncTask类的主要实现就介绍完了,结合前面开发的应用程序Counter来分析,会更好地理解它的实现原理。

至此,Android应用程序线程消息循环模型就分析完成了,理解它有利于我们在开发Android应用程序时,能够充分利用多线程的并发性来提高应用程序的性能以及获得良好的用户体验。

老罗的新浪微博:http://weibo.com/shengyangluo,欢迎关注!

时间: 2024-11-07 21:21:31

Android应用程序线程消息循环模型分析的相关文章

Android----Thread+Handler 线程 消息循环(转载)

近来找了一些关于android线程间通信的资料,整理学习了一下,并制作了一个简单的例子. andriod提供了 Handler 和 Looper 来满足线程间的通信.例如一个子线程从网络上下载了一副图片,当它下载完成后会发送消息给主线程,这个消息是通过绑定在主线程的Handler来传递的. 在Android,这里的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个事android的新 概念.我们的主线程(UI线程)就是一个消息循环的线程.针对这种消息循

【从源码看Android】03Android MessageQueue消息循环处理机制(epoll实现)

1 enqueueMessage handler发送一条消息 mHandler.sendEmptyMessage(1); 经过层层调用,进入到sendMessageAtTime函数块,最后调用到enqueueMessage Handler.java public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeE

Thread+Handler 线程 消息循环(转载)

近来找了一些关于android线程间通信的资料,整理学习了一下,并制作了一个简单的例子. andriod提供了 Handler 和 Looper 来满足线程间的通信.例如一个子线程从网络上下载了一副图片,当它下载完成后会发送消息给主线程,这个消息是通过绑定在主线程的Handler来传递的. 在Android,这里的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper,这个事android的新 概念.我们的主线程(UI线程)就是一个消息循环的线程.针对这种消息循

创建线程消息循环服务于动态连接库

因为动态连接库需要对DBT_DEVICEARRIVAL.DBT_DEVICEREMOVECOMPLETE等消息的处理,所以拿一个不太理想的usb-hid的dll来说明,不多说直接上代码 1 // 下列 ifdef 块是创建使从 DLL 导出更简单的 2 // 宏的标准方法.此 DLL 中的所有文件都是用命令行上定义的 WIN32USB_EXPORTS 3 // 符号编译的.在使用此 DLL 的 4 // 任何其他项目上不应定义此符号.这样,源文件中包含此文件的任何其他项目都会将 5 // WIN

浅谈使用tcpdump、wireshark对Android应用程序进行抓包并分析

本文主要介绍如何使用tcpdump和wireshark对Android应用程序进行抓包并分析,需要说明的是在抓包之前,你的Android设备必须root过了,另外你的电脑必须有Android SDK环境. 下载并安装tcpdump tcpdump链接:http://www.ijiami.cn/ 选择一个版本下载并解压提取出其中呃tcpdump文件,然后push到你的手机上去: adb push c:\tcpdump /data/local/tcpdump 进一步操作: adb shell su

Android IntentService源码理解 及 HandlerThread构建消息循环机制分析

前言:前面写了Handler的源码理解,关于Handler在我们Android开发中是到处能见到的异步通信方式.那么,在Android原生里,有那些也有到了Handler机制的呢?有很多,比如我们今天所要理解分析的IntentService就使用到了Handler.接下来,我们来深入了解一下. HandlerThread: IntentService使用到了Handler+HandlerThread构建的带有消息循环的异步任务处理机制,我们先简单看一下HandlerThread是如何工作的吧.

Android应用程序键盘(Keyboard)消息处理机制分析

在Android系统中,键盘按键事件是由WindowManagerService服务来管理的,然后再以消息的形 式来分发给应用程序处理,不过和普通消息不一样,它是由硬件中断触发的:在上一篇文章<Android应用程序消息处理机制(Looper. Handler)分析>中,我们分析了Android应用程序的消息处理机制,本文将结合这种消息处理机制来详细分析Android应用程序是如何获得键 盘按键消息的. 在系统启动的时候,SystemServer会启动窗口管理服务WindowManagerSe

Android系统Surface机制的SurfaceFlinger服务的线程模型分析

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8062945 在前面两篇文章中,我们分析了SurfaceFlinger服务的启动过程以及SurfaceFlinger服务初始化硬件帧缓冲区的过程.从这两个过程可以知道,SurfaceFlinger服务在启动的过程中,一共涉及到了三种类型的线程,它们分别是Binder线程.UI渲染线程和控制台事件监控线程.在本文中,我们就将详细分SurfaceFl

Chromium on Android: Android系统上Chromium主消息循环的实现分析

摘要:刚一开始接触Chromium on Android时,就很好奇Chromium的主消息循环是怎么整合到Android应用程序中的.对于Android程序来说,一旦启动,主线程就会有一个Java层的消息循环处理用户输入事件等系统事件,而对Chromium来说,它有自己另一套消息循环的实现,这个实现有哪些特点,又将如何无缝整合到Android Java层的消息循环中去,正是本文所要讨论的话题. 原创文章系列,转载请注明原始出处为http://blog.csdn.net/hongbomin/ar