我們知道,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為當前應用程序創建一個主線程:
view plaincopy to clipboardprint?public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
……
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
……
try {
int uid = app.info.uid;
int[] gids = null;
try {
gids = mContext.getPackageManager().getPackageGids(
app.info.packageName);
} catch (PackageManager.NameNotFoundException e) {
……
}
……
int debugFlags = 0;
……
int pid = Process.start("android.app.ActivityThread",
mSimpleProcessManagement ? app.processName : null, uid, uid,
gids, debugFlags, null);
……
} catch (RuntimeException e) {
……
}
}
……
}
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
……
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
……
try {
int uid = app.info.uid;
int[] gids = null;
try {
gids = mContext.getPackageManager().getPackageGids(
app.info.packageName);
} catch (PackageManager.NameNotFoundException e) {
……
}
……
int debugFlags = 0;
……
int pid = Process.start("android.app.ActivityThread",
mSimpleProcessManagement ? app.processName : null, uid, uid,
gids, debugFlags, null);
……
} catch (RuntimeException e) {
……
}
}
……
} 這裡我們主要關註Process.start函數的第一個參數“android.app.ActivityThread”,它表示要在當前新建的線程中加載android.app.ActivityThread類,並且調用這個類的靜態成員函數main作為應用程序的入口點。ActivityThread類定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:
view plaincopy to clipboardprint?public final class ActivityThread {
……
public static final void main(String[] args) {
……
Looper.prepareMainLooper();
……
ActivityThread thread = new ActivityThread();
thread.attach(false);
……
Looper.loop();
……
thread.detach();
……
}
……
}
public final class ActivityThread {
……
public static final void main(String[] args) {
……
Looper.prepareMainLooper();
……
ActivityThread thread = new ActivityThread();
thread.attach(false);
……
Looper.loop();
……
thread.detach();
……
}
……
} 在這個main函數裡面,除瞭創建一個ActivityThread實例外,就是在進行消息循環瞭。
在進行消息循環之前,首先會通過Looper類的靜態成員函數prepareMainLooper為當前線程準備一個消息循環對象。Looper類定義在frameworks/base/core/java/android/os/Looper.java文件中:
view plaincopy to clipboardprint?public class Looper {
……
// sThreadLocal.get() will return null unless you've called prepare().
private static final ThreadLocal sThreadLocal = new ThreadLocal();
……
private static Looper mMainLooper = null;
……
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
……
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
……
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
public synchronized static final Looper getMainLooper() {
return mMainLooper;
}
……
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
……
}
public class Looper {
……
// sThreadLocal.get() will return null unless you've called prepare().
private static final ThreadLocal sThreadLocal = new ThreadLocal();
……
private static Looper mMainLooper = null;
……
public static final void prepare() {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper());
}
……
public static final void prepareMainLooper() {
prepare();
setMainLooper(myLooper());
……
}
private synchronized static void setMainLooper(Looper looper) {
mMainLooper = looper;
}
public synchronized static final Looper getMainLooper() {
return mMainLooper;
}
……
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
……
}
Looper類的靜態成員函數prepareMainLooper是專門應用程序的主線程調用的,應用程序的其它子線程都不應該調用這個函數來在本線程中創建消息循環對象,而應該調用prepare函數來在本線程中創建消息循環對象,下一節我們介紹一個線程類HandlerThread 時將會看到。
為什麼要為應用程序的主線程專門準備一個創建消息循環對象的函數呢?這是為瞭讓其它地方能夠方便地通過Looper類的getMainLooper函數來獲得應用程序主線程中的消息循環對象。獲得應用程序主線程中的消息循環對象又有什麼用呢?一般就是為瞭能夠向應用程序主線程發送消息瞭。
在prepareMainLooper函數中,首先會調用prepare函數在本線程中創建一個消息循環對象,然後將這個消息循環對象放在線程局部變量sThreadLocal中:
view plaincopy to clipboardprint?sThreadLocal.set(new Looper());
sThreadLocal.set(new Looper()); 接著再將這個消息循環對象通過調用setMainLooper函數來保存在Looper類的靜態成員變量mMainLooper中:
view plaincopy to clipboardprint?mMainLooper = looper;
mMainLooper = looper; 這樣,其它地方才可以調用getMainLooper函數來獲得應用程序主線程中的消息循環對象。
消息循環對象創建好之後,回到ActivityThread類的main函數中,接下來,就是要進入消息循環瞭:
view plaincopy to clipboardprint?Looper.loop();
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類來實現在一個子線程加載系統中已經安裝的應用程序的任務:
view plaincopy to clipboardprint?public class LauncherModel extends BroadcastReceiver {
……
private LoaderTask mLoaderTask;
private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
sWorkerThread.start();
}
private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
……
public void startLoader(Context context, boolean isLaunching) {
……
synchronized (mLock) {
……
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
……
mLoaderTask = new LoaderTask(context, isLaunching);
sWorker.post(mLoaderTask);
}
}
}
……
private class LoaderTask implements Runnable {
……
public void run() {
……
keep_running: {
……
// second step
if (loadWorkspaceFirst) {
……
loadAndBindAllApps();
} else {
……
}
……
}
……
}
……
}
……
}
public class LauncherModel extends BroadcastReceiver {
……
private LoaderTask mLoaderTask;
private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
sWorkerThread.start();
}
private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
……
public void startLoader(Context context, boolean isLaunching) {
……
synchronized (mLock) {
……
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
……
mLoaderTask = new LoaderTask(context, isLaunching);
sWorker.post(mLoaderTask);
}
}
}
……
private class LoaderTask implements Runnable {
……
public void run() {
……
keep_running: {
……
// second step
if (loadWorkspaceFirst) {
……
loadAndBindAllApps();
} else {
……
}
……
}
……
}
……
}
……
} 在這個LauncherModel類中,首先創建瞭一個HandlerThread對象:
view plaincopy to clipboardprint?private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader"); 接著調用它的start成員函數來啟動一個子線程:
view plaincopy to clipboardprint?static {
sWorkerThread.start();
}
static {
sWorkerThread.start();
} 接著還通過這個HandlerThread對象的getLooper函數來獲得這個子線程中的消息循環對象,並且使用這個消息循環創建對象來創建一個Handler:
view plaincopy to clipboardprint?private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
private static final Handler sWorker = new Handler(sWorkerThread.getLooper()); 有瞭這個Handler對象sWorker之後,我們就可以往這個子線程中發送消息,然後在處理這個消息的時候執行加載系統中已經安裝的應用程序的任務瞭,在startLoader函數中:
view plaincopy to clipboardprint?mLoaderTask = new LoaderTask(context, isLaunching);
sWorker.post(mLoaderTask);
mLoaderTask = new LoaderTask(context, isLaunching);
sWorker.post(mLoaderTask); 這裡的mLoaderTask是一個LoaderTask對象,它實現瞭Runnable接口,因此,可以把這個LoaderTask對象作為參數傳給sWorker.post函數。在sWorker.post函數裡面,會把這個LoaderTask對象封裝成一個消息,並且放入這個子線程的消息隊列中去。當這個子線程的消息循環處理這個消息的時候,就會調用這個LoaderTask對象的run函數,因此,我們就可以在LoaderTask對象的run函數中通過調用loadAndBindAllApps來執行加載系統中已經安裝的應用程序的任務瞭。
瞭解瞭HanderThread類的使用方法之後,我們就可以重點地來分析它的實現瞭:
view plaincopy to clipboardprint?public class HandlerThread extends Thread {
……
private Looper mLooper;
public HandlerThread(String name) {
super(name);
……
}
……
public void run() {
……
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
……
}
……
Looper.loop();
……
}
public Looper getLooper() {
……
return mLooper;
}
……
}
public class HandlerThread extends Thread {
……
private Looper mLooper;
public HandlerThread(String name) {
super(name);
……
}
……
public void run() {
……
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
……
}
……
Looper.loop();
……
}
public Looper getLooper() {
……
return mLooper;
}
……
} 首先我們看到的是,Handler類繼承瞭Thread類,因此,通過它可以在應用程序中創建一個子線程,其次我們看到在它的run函數中,會進入一個消息循環中,因此,這個子線程可以常駐在應用程序中,直到它接收收到一個退出消息為止。
在run函數中,首先是調用Looper類的靜態成員函數prepare來準備一個消息循環對象:
view plaincopy to clipboardprint?Looper.prepare();
Looper.prepare(); 然後通過Looper類的myLooper成員函數將這個子線程中的消息循環對象保存在HandlerThread類中的成員變量mLooper中:
view plaincopy to clipboardprint?mLooper = Looper.myLooper();
mLooper = Looper.myLooper(); 這樣,其它地方就可以方便地通過它的getLooper函數來獲得這個消息循環對象瞭,有瞭這個消息循環對象後,就可以往這個子線程的消息隊列中發送消息,通知這個子線程執行特定的任務瞭。
最在這個run函數通過Looper類的loop函數進入消息循環中:
view plaincopy to clipboardprint?Looper.loop();
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文件中:
view plaincopy to clipboardprint?package shy.luo.counter;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class Counter extends Activity implements OnClickListener {
private final static String LOG_TAG = "shy.luo.counter.Counter";
private Button startButton = null;
private Button stopButton = null;
private TextView counterText = null;
private AsyncTask<Integer, Integer, Integer> task = null;
private boolean stop = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startButton = (Button)findViewById(R.id.button_start);
stopButton = (Button)findViewById(R.id.button_stop);
counterText = (TextView)findViewById(R.id.textview_counter);
startButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
startButton.setEnabled(true);
stopButton.setEnabled(false);
Log.i(LOG_TAG, "Main Activity Created.");
}
@Override
public void onClick(View v) {
if(v.equals(startButton)) {
if(task == null) {
task = new CounterTask();
task.execute(0);
startButton.setEnabled(false);
stopButton.setEnabled(true);
}
} else if(v.equals(stopButton)) {
if(task != null) {
stop = true;
task = null;
startButton.setEnabled(true);
stopButton.setEnabled(false);
}
}
}
class CounterTask extends AsyncTask<Integer, Integer, Integer> {
@Override
protected Integer doInBackground(Integer… vals) {
Integer initCounter = vals[0];
stop = false;
while(!stop) {
publishProgress(initCounter);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
initCounter++;
}
return initCounter;
}
@Override
protected void onProgressUpdate(Integer… values) {
super.onProgressUpdate(values);
String text = values[0].toString();
counterText.setText(text);
}
@Override
protected void onPostExecute(Integer val) {
String text = val.toString();
counterText.setText(text);
}
};
}
package shy.luo.counter;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class Counter extends Activity implements OnClickListener {
private final static String LOG_TAG = "shy.luo.counter.Counter";
private Button startButton = null;
private Button stopButton = null;
private TextView counterText = null;
private AsyncTask<Integer, Integer, Integer> task = null;
private boolean stop = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startButton = (Button)findViewById(R.id.button_start);
stopButton = (Button)findViewById(R.id.button_stop);
counterText = (TextView)findViewById(R.id.textview_counter);
startButton.setOnClickListener(this);
stopButton.setOnClickListener(this);
startButton.setEnabled(true);
stopButton.setEnabled(false);
Log.i(LOG_TAG, "Main Activity Created.");
}
@Override
public void onClick(View v) {
if(v.equals(startButton)) {
if(task == null) {
task = new CounterTask();
task.execute(0);
startButton.setEnabled(false);
stopButton.setEnabled(true);
}
} else if(v.equals(stopButton)) {
if(task != null) {
stop = true;
task = null;
startButton.setEnabled(true);
stopButton.setEnabled(false);
}
}
}
class CounterTask extends AsyncTask<Integer, Integer, Integer> {
@Override
protected Integer doInBackground(Integer… vals) {
Integer initCounter = vals[0];
stop = false;
while(!stop) {
publishProgress(initCounter);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
initCounter++;
}
return initCounter;
}
@Override
protected void onProgressUpdate(Integer… values) {
super.onProgressUpdate(values);
String text = values[0].toString();
counterText.setText(text);
}
@Override
protected void onPostExecute(Integer val) {
String text = val.toString();
counterText.setText(text);
}
};
} 這個計數器程序很簡單,它在界面上有兩個按鈕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:
view plaincopy to clipboardprint?<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="shy.luo.counter"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Counter"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="shy.luo.counter"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".Counter"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest> 這個配置文件很簡單,我們就不介紹瞭。
再來看應用程序的界面文件,它定義在res/layout/main.xml文件中:
view plaincopy to clipboardprint?<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10px"
android:orientation="horizontal"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4px"
android:gravity="center"
android:text="@string/counter">
</TextView>
<TextView
android:id="@+id/textview_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="0">
</TextView>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<Button
android:id="@+id/button_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/start">
</Button>
<Button
android:id="@+id/button_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/stop" >
</Button>
</LinearLayout>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10px"
android:orientation="horizontal"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="4px"
android:gravity="center"
android:text="@string/counter">
</TextView>
<TextView
android:id="@+id/textview_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="0">
</TextView>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<Button
android:id="@+id/button_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/start">
</Button>
<Button
android:id="@+id/button_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="@string/stop" >
</Button>
</LinearLayout>
</LinearLayout> 這個界面配置文件也很簡單,等一下我們在模擬器把這個應用程序啟動起來後,就可以看到它的截圖瞭。
應用程序用到的字符串資源文件位於res/values/strings.xml文件中:
view plaincopy to clipboardprint?<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Counter</string>
<string name="counter">Counter: </string>
<string name="start">Start Counter</string>
<string name="stop">Stop Counter</string>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Counter</string>
<string name="counter">Counter: </string>
<string name="start">Start Counter</string>
<string name="stop">Stop Counter</string>
</resources> 最後,我們還要在工程目錄下放置一個編譯腳本文件Android.mk:
view plaincopy to clipboardprint?LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := Counter
include $(BUILD_PACKAGE)
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := Counter
include $(BUILD_PACKAGE) 接下來就要編譯瞭。有關如何單獨編譯Android源代碼工程的模塊,以及如何打包system.img,請參考如何單獨編譯Android源代碼中的模塊一文。
執行以下命令進行編譯和打包:
view plaincopy to clipboardprint?USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Counter
USER-NAME@MACHINE-NAME:~/Android$ make snod
USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Counter
USER-NAME@MACHINE-NAME:~/Android$ make snod 這樣,打包好的Android系統鏡像文件system.img就包含我們前面創建的Counter應用程序瞭。
再接下來,就是運行模擬器來運行我們的例子瞭。關於如何在Android源代碼工程中運行模擬器,請參考在Ubuntu上下載、編譯和安裝Android最新源代碼一文。
執行以下命令啟動模擬器:
view plaincopy to clipboardprint?USER-NAME@MACHINE-NAME:~/Android$ emulator
USER-NAME@MACHINE-NAME:~/Android$ emulator 最後我們就可以在Launcher中找到Counter應用程序圖標,把它啟動起來,點擊Start按鈕,就會看到應用程序界面上的計數器跑起來瞭:
這樣,使用AsyncTask的例子就介紹完瞭,下面,我們就要根據上面對AsyncTask的使用情況來重點分析它的實現瞭。
AsyncTask類定義在frameworks/base/core/java/android/os/AsyncTask.java文件中:
view plaincopy to clipboardprint?public abstract class AsyncTask<Params, Progress, Result> {
……
private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
……
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static final int MESSAGE_POST_CANCEL = 0x3;
private static final InternalHandler sHandler = new InternalHandler();
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
……
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
……
return doInBackground(mParams);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
Message message;
Result result = null;
try {
result = get();
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
message.sendToTarget();
return;
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(AsyncTask.this, result));
message.sendToTarget();
}
};
}
……
public final Result get() throws InterruptedException, ExecutionException {
return mFuture.get();
}
……
public final AsyncTask<Params, Progress, Result> execute(Params… params) {
……
mWorker.mParams = params;
sExecutor.execute(mFuture);
return this;
}
……
protected final void publishProgress(Progress… values) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
private void finish(Result result) {
……
onPostExecute(result);
……
}
……
private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
case MESSAGE_POST_CANCEL:
result.mTask.onCancelled();
break;
}
}
}
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data… data) {
mTask = task;
mData = data;
}
}
}
public abstract class AsyncTask<Params, Progress, Result> {
……
private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
……
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
private static final int MESSAGE_POST_CANCEL = 0x3;
private static final InternalHandler sHandler = new InternalHandler();
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
……
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
……
return doInBackground(mParams);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
Message message;
Result result = null;
try {
result = get();
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
message.sendToTarget();
return;
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(AsyncTask.this, result));
message.sendToTarget();
}
};
}
……
public final Result get() throws InterruptedException, ExecutionException {
return mFuture.get();
}
……
public final AsyncTask<Params, Progress, Result> execute(Params… params) {
……
mWorker.mParams = params;
sExecutor.execute(mFuture);
return this;
}
……
protected final void publishProgress(Progress… values) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
private void finish(Result result) {
……
onPostExecute(result);
……
}
……
private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
case MESSAGE_POST_CANCEL:
result.mTask.onCancelled();
break;
}
}
}
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data… data) {
mTask = task;
mData = data;
}
}
}
從AsyncTask的實現可以看出,當我們第一次創建一個AsyncTask對象時,首先會執行下面靜態初始化代碼創建一個線程池sExecutor:
view plaincopy to clipboardprint?private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
……
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
……
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory); 這裡的ThreadPoolExecutor是Java提供的多線程機制之一,這裡用的構造函數原型為:
view plaincopy to clipboardprint?ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
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也已經滿瞭,這個工作任務就被拒絕執行瞭。
創建好瞭線程池後,再創建一個消息處理器:
view plaincopy to clipboardprint?private static final InternalHandler sHandler = new InternalHandler();
private static final InternalHandler sHandler = new InternalHandler(); 註意,這行代碼是在應用程序的主線程中執行的,因此,這個消息處理器sHandler內部引用的消息循環對象looper是應用程序主線程的消息循環對象,消息處理器的實現機制具體可以參考前面一篇文章Android應用程序消息處理機制(Looper、Handler)分析。
AsyncTask類的靜態初始化代碼執行完成之後,才開始創建AsyncTask對象,即執行AsyncTask類的構造函數:
view plaincopy to clipboardprint?public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
……
return doInBackground(mParams);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
Message message;
Result result = null;
try {
result = get();
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
message.sendToTarget();
return;
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(AsyncTask.this, result));
message.sendToTarget();
}
};
}
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
……
return doInBackground(mParams);
}
};
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
Message message;
Result result = null;
try {
result = get();
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
message.sendToTarget();
return;
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(AsyncTask.this, result));
message.sendToTarget();
}
};
} 在AsyncTask類的構造函數裡面,主要是創建瞭兩個對象,分別是一個WorkerRunnable對象mWorker和一個FutureTask對象mFuture。
WorkerRunnable類實現瞭Runnable接口,此外,它的內部成員變量mParams用於保存從AsyncTask對象的execute函數傳進來的參數列表:
view plaincopy to clipboardprint?private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
} FutureTask類也實現瞭Runnable接口,所以它可以作為一個工作任務通過調用AsyncTask類的execute函數添加到sExecuto線程池中去:
view plaincopy to clipboardprint?public final AsyncTask<Params, Progress, Result> execute(Params… params) {
……
mWorker.mParams = params;
sExecutor.execute(mFuture);
return this;
}
public final AsyncTask<Params, Progress, Result> execute(Params… params) {
……
mWorker.mParams = params;
sExecutor.execute(mFuture);
return this;
}
這裡的FutureTask對象mFuture是用來封裝前面的WorkerRunnable對象mWorker。當mFuture加入到線程池中執行時,它調用的是mWorker對象的call函數:
view plaincopy to clipboardprint?mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
……
return doInBackground(mParams);
}
};
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
……
return doInBackground(mParams);
}
}; 在call函數裡面,會調用AsyncTask類的doInBackground函數來執行真正的任務,這個函數是要由AsyncTask的子類來實現的,註意,這個函數是在應用程序的子線程中執行的,它不可以操作應用程序的界面。
我們可以通過mFuture對象來操作當前執行的任務,例如查詢當前任務的狀態,它是正在執行中,還是完成瞭,還是被取消瞭,如果是完成瞭,還可以通過它獲得任務的執行結果,如果還沒有完成,可以取消任務的執行。
當工作任務mWorker執行完成的時候,mFuture對象中的done函數就會被被調用,根據任務的完成狀況,執行相應的操作,例如,如果是因為異常而完成時,就會拋異常,如果是正常完成,就會把任務執行結果封裝成一個AsyncTaskResult對象:
view plaincopy to clipboardprint?private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data… data) {
mTask = task;
mData = data;
}
}
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data… data) {
mTask = task;
mData = data;
}
} 其中,成員變量mData保存的是任務執行結果,而成員變量mTask指向前面我們創建的AsyncTask對象。
最後把這個AsyncTaskResult對象封裝成一個消息,並且通過消息處理器sHandler加入到應用程序主線程的消息隊列中:
view plaincopy to clipboardprint?message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(AsyncTask.this, result));
message.sendToTarget();
message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(AsyncTask.this, result));
message.sendToTarget(); 這個消息最終就會在InternalHandler類的handleMessage函數中處理瞭:
view plaincopy to clipboardprint?private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
……
}
}
}
private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
……
}
}
} 在這個函數裡面,最終會調用前面創建的這個AsyncTask對象的finish函數來進一步處理:
view plaincopy to clipboardprint?private void finish(Result result) {
……
onPostExecute(result);
……
}
private void finish(Result result) {
……
onPostExecute(result);
……
} 這個函數調用AsyncTask類的onPostExecute函數來進一步處理,AsyncTask類的onPostExecute函數一般是要由其子類來重載的,註意,這個函數是在應用程序的主線程中執行的,因此,它可以操作應用程序的界面。
在任務執行的過程當中,即執行doInBackground函數時候,可能通過調用publishProgress函數來將中間結果封裝成一個消息發送到應用程序主線程中的消息隊列中去:
view plaincopy to clipboardprint?protected final void publishProgress(Progress… values) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
protected final void publishProgress(Progress… values) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
} 這個消息最終也是由InternalHandler類的handleMessage函數來處理的:
view plaincopy to clipboardprint?private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
……
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
……
}
}
}
private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
……
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
……
}
}
} 這裡它調用前面創建的AsyncTask對象的onPorgressUpdate函數來進一步處理,這個函數一般是由AsyncTask的子類來實現的,註意,這個函數是在應用程序的主線程中執行的,因此,它和前面的onPostExecute函數一樣,可以操作應用程序的界面。
這樣,AsyncTask類的主要實現就介紹完瞭,結合前面開發的應用程序Counter來分析,會更好地理解它的實現原理。
至此,Android應用程序線程消息循環模型就分析完成瞭,理解它有利於我們在開發Android應用程序時,能夠充分利用多線程的並發性來提高應用程序的性能以及獲得良好的用戶體驗。
作者 :不想睡的豬