posts - 101,  comments - 29,  trackbacks - 0

                  我們知道,Android應(yīng)用程序是通過消息來驅(qū)動(dòng)的,即在應(yīng)用程序的主線程(UI線程)中有一個(gè)消息循環(huán),負(fù)責(zé)處理消息隊(duì)列中的消息。我們也知道,Android應(yīng)用程序是支持多線程的,即可以創(chuàng)建子線程來執(zhí)行一些計(jì)算型的任務(wù),那么,這些子線程能不能像應(yīng)用程序的主線程一樣具有消息循環(huán)呢?這些子線程又能不能往應(yīng)用程序的主線程中發(fā)送消息呢?本文將分析Android應(yīng)用程序線程消息處理模型,為讀者解答這兩個(gè)問題

                  在開發(fā)Android應(yīng)用程序中,有時(shí)候我們需要在應(yīng)用程序中創(chuàng)建一些常駐的子線程來不定期地執(zhí)行一些不需要與應(yīng)用程序界面交互的計(jì)算型的任務(wù)。如果這些子線程具有消息循環(huán),那么它們就能夠常駐在應(yīng)用程序中不定期的執(zhí)行一些計(jì)算型任務(wù)了:當(dāng)我們需要用這些子線程來執(zhí)行任務(wù)時(shí),就往這個(gè)子線程的消息隊(duì)列中發(fā)送一個(gè)消息,然后就可以在子線程的消息循環(huán)中執(zhí)行我們的計(jì)算型任務(wù)了。我們?cè)谇懊嬉黄恼?a >Android系統(tǒng)默認(rèn)Home應(yīng)用程序(Launcher)的啟動(dòng)過程源代碼分析中,介紹Launcher的啟動(dòng)過程時(shí),在Step 15(LauncherModel.startLoader)中,Launcher就是通過往一個(gè)子線程的消息隊(duì)列中發(fā)送一個(gè)消息(sWorker.post(mLoaderTask)),然后子線程就會(huì)在它的消息循環(huán)中處理這個(gè)消息的時(shí)候執(zhí)行從PackageManagerService中獲取系統(tǒng)中已安裝應(yīng)用程序的信息列表的任務(wù),即調(diào)用Step 16中的LoaderTask.run函數(shù)。

                  在開發(fā)Android應(yīng)用程序中,有時(shí)候我們又需要在應(yīng)用程序中創(chuàng)建一些子線程來執(zhí)行一些需要與應(yīng)用程序界面進(jìn)交互的計(jì)算型任務(wù)。典型的應(yīng)用場景是當(dāng)我們要從網(wǎng)上下載文件時(shí),為了不使主線程被阻塞,我們通常創(chuàng)建一個(gè)子線程來負(fù)責(zé)下載任務(wù),同時(shí),在下載的過程,將下載進(jìn)度以百分比的形式在應(yīng)用程序的界面上顯示出來,這樣就既不會(huì)阻塞主線程的運(yùn)行,又能獲得良好的用戶體驗(yàn)。但是,我們知道,Android應(yīng)用程序的子線程是不可以操作主線程的UI的,那么,這個(gè)負(fù)責(zé)下載任務(wù)的子線程應(yīng)該如何在應(yīng)用程序界面上顯示下載的進(jìn)度呢?如果我們能夠在子線程中往主線程的消息隊(duì)列中發(fā)送消息,那么問題就迎刃而解了,因?yàn)榘l(fā)往主線程消息隊(duì)列的消息最終是由主線程來處理的,在處理這個(gè)消息的時(shí)候,我們就可以在應(yīng)用程序界面上顯示下載進(jìn)度了。

                  上面提到的這兩種情況,Android系統(tǒng)都為我們提供了完善的解決方案,前者可以通過使用HandlerThread類來實(shí)現(xiàn),而后者可以使用AsyncTask類來實(shí)現(xiàn),本文就詳細(xì)這兩個(gè)類是如何實(shí)現(xiàn)的。不過,為了更好地理解HandlerThread類和AsyncTask類的實(shí)現(xiàn),我們先來看看應(yīng)用程序的主線程的消息循環(huán)模型是如何實(shí)現(xiàn)的。

                  1. 應(yīng)用程序主線程消息循環(huán)模型

                  在前面一篇文章Android應(yīng)用程序進(jìn)程啟動(dòng)過程的源代碼分析一文中,我們已經(jīng)分析應(yīng)用程序進(jìn)程(主線程)的啟動(dòng)過程了,這里主要是針對(duì)它的消息循環(huán)模型作一個(gè)總結(jié)。當(dāng)運(yùn)行在Android應(yīng)用程序框架層中的ActivityManagerService決定要為當(dāng)前啟動(dòng)的應(yīng)用程序創(chuàng)建一個(gè)主線程的時(shí)候,它會(huì)在ActivityManagerService中的startProcessLocked成員函數(shù)調(diào)用Process類的靜態(tài)成員函數(shù)start為當(dāng)前應(yīng)用程序創(chuàng)建一個(gè)主線程:

           

          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) {    
                          
                      ......    
              
                  }    
              }    
              
              ......    
              
          }    
                  這里我們主要關(guān)注Process.start函數(shù)的第一個(gè)參數(shù)“android.app.ActivityThread”,它表示要在當(dāng)前新建的線程中加載android.app.ActivityThread類,并且調(diào)用這個(gè)類的靜態(tài)成員函數(shù)main作為應(yīng)用程序的入口點(diǎn)。ActivityThread類定義在frameworks/base/core/java/android/app/ActivityThread.java文件中:

           

           

          public final class ActivityThread {  
              ......  
            
              public static final void main(String[] args) {  
                  ......
            
                  Looper.prepareMainLooper();  
                   
                  ......  
            
                  ActivityThread thread = new ActivityThread();  
                  thread.attach(false);  
            
                  ...... 
                  Looper.loop();  
            
                  ...... 
            
                  thread.detach();  
                  ......  
              }  
            
              ......  
          }  
                  在這個(gè)main函數(shù)里面,除了創(chuàng)建一個(gè)ActivityThread實(shí)例外,就是在進(jìn)行消息循環(huán)了。

           

                  在進(jìn)行消息循環(huán)之前,首先會(huì)通過Looper類的靜態(tài)成員函數(shù)prepareMainLooper為當(dāng)前線程準(zhǔn)備一個(gè)消息循環(huán)對(duì)象。Looper類定義在frameworks/base/core/java/android/os/Looper.java文件中:

           

          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類的靜態(tài)成員函數(shù)prepareMainLooper是專門應(yīng)用程序的主線程調(diào)用的,應(yīng)用程序的其它子線程都不應(yīng)該調(diào)用這個(gè)函數(shù)來在本線程中創(chuàng)建消息循環(huán)對(duì)象,而應(yīng)該調(diào)用prepare函數(shù)來在本線程中創(chuàng)建消息循環(huán)對(duì)象,下一節(jié)我們介紹一個(gè)線程類HandlerThread 時(shí)將會(huì)看到。

                  為什么要為應(yīng)用程序的主線程專門準(zhǔn)備一個(gè)創(chuàng)建消息循環(huán)對(duì)象的函數(shù)呢?這是為了讓其它地方能夠方便地通過Looper類的getMainLooper函數(shù)來獲得應(yīng)用程序主線程中的消息循環(huán)對(duì)象。獲得應(yīng)用程序主線程中的消息循環(huán)對(duì)象又有什么用呢?一般就是為了能夠向應(yīng)用程序主線程發(fā)送消息了。

                  在prepareMainLooper函數(shù)中,首先會(huì)調(diào)用prepare函數(shù)在本線程中創(chuàng)建一個(gè)消息循環(huán)對(duì)象,然后將這個(gè)消息循環(huán)對(duì)象放在線程局部變量sThreadLocal中:

           

          sThreadLocal.set(new Looper());
                  接著再將這個(gè)消息循環(huán)對(duì)象通過調(diào)用setMainLooper函數(shù)來保存在Looper類的靜態(tài)成員變量mMainLooper中:

           

           

          mMainLooper = looper;
                 這樣,其它地方才可以調(diào)用getMainLooper函數(shù)來獲得應(yīng)用程序主線程中的消息循環(huán)對(duì)象。

           

                 消息循環(huán)對(duì)象創(chuàng)建好之后,回到ActivityThread類的main函數(shù)中,接下來,就是要進(jìn)入消息循環(huán)了:

           

           Looper.loop(); 
                  Looper類具體是如何通過loop函數(shù)進(jìn)入消息循環(huán)以及處理消息隊(duì)列中的消息,可以參考前面一篇文章Android應(yīng)用程序消息處理機(jī)制(Looper、Handler)分析,這里就不再分析了,我們只要知道ActivityThread類中的main函數(shù)執(zhí)行了這一步之后,就為應(yīng)用程序的主線程準(zhǔn)備好消息循環(huán)就可以了。

           

                  2. 應(yīng)用程序子線程消息循環(huán)模型

                  在Java框架中,如果我們想在當(dāng)前應(yīng)用程序中創(chuàng)建一個(gè)子線程,一般就是通過自己實(shí)現(xiàn)一個(gè)類,這個(gè)類繼承于Thread類,然后重載Thread類的run函數(shù),把我們想要在這個(gè)子線程執(zhí)行的任務(wù)都放在這個(gè)run函數(shù)里面實(shí)現(xiàn)。最后實(shí)例這個(gè)自定義的類,并且調(diào)用它的start函數(shù),這樣一個(gè)子線程就創(chuàng)建好了,并且會(huì)調(diào)用這個(gè)自定義類的run函數(shù)。但是當(dāng)這個(gè)run函數(shù)執(zhí)行完成后,子線程也就結(jié)束了,它沒有消息循環(huán)的概念。

                  前面說過,有時(shí)候我們需要在應(yīng)用程序中創(chuàng)建一些常駐的子線程來不定期地執(zhí)行一些計(jì)算型任務(wù),這時(shí)候就可以考慮使用Android系統(tǒng)提供的HandlerThread類了,它具有創(chuàng)建具有消息循環(huán)功能的子線程的作用。

                  HandlerThread類實(shí)現(xiàn)在frameworks/base/core/java/android/os/HandlerThread.java文件中,這里我們通過使用情景來有重點(diǎn)的分析它的實(shí)現(xiàn)。

                  在前面一篇文章Android系統(tǒng)默認(rèn)Home應(yīng)用程序(Launcher)的啟動(dòng)過程源代碼分析中,我們分析了Launcher的啟動(dòng)過程,其中在Step 15(LauncherModel.startLoader)和Step 16(LoaderTask.run)中,Launcher會(huì)通過創(chuàng)建一個(gè)HandlerThread類來實(shí)現(xiàn)在一個(gè)子線程加載系統(tǒng)中已經(jīng)安裝的應(yīng)用程序的任務(wù):

           

          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 {  
          					......  
          				}  
          
          				......  
          			}  
          
          			......  
          		}  
          
          		......  
          	} 
          
          	......
          }
                  在這個(gè)LauncherModel類中,首先創(chuàng)建了一個(gè)HandlerThread對(duì)象:

           

           

          private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
                  接著調(diào)用它的start成員函數(shù)來啟動(dòng)一個(gè)子線程:

           

           

          static {
              sWorkerThread.start();
          }
                  接著還通過這個(gè)HandlerThread對(duì)象的getLooper函數(shù)來獲得這個(gè)子線程中的消息循環(huán)對(duì)象,并且使用這個(gè)消息循環(huán)創(chuàng)建對(duì)象來創(chuàng)建一個(gè)Handler:

           

           

          private static final Handler sWorker = new Handler(sWorkerThread.getLooper());
                  有了這個(gè)Handler對(duì)象sWorker之后,我們就可以往這個(gè)子線程中發(fā)送消息,然后在處理這個(gè)消息的時(shí)候執(zhí)行加載系統(tǒng)中已經(jīng)安裝的應(yīng)用程序的任務(wù)了,在startLoader函數(shù)中:

           

           

          mLoaderTask = new LoaderTask(context, isLaunching);  
          sWorker.post(mLoaderTask);  
                  這里的mLoaderTask是一個(gè)LoaderTask對(duì)象,它實(shí)現(xiàn)了Runnable接口,因此,可以把這個(gè)LoaderTask對(duì)象作為參數(shù)傳給sWorker.post函數(shù)。在sWorker.post函數(shù)里面,會(huì)把這個(gè)LoaderTask對(duì)象封裝成一個(gè)消息,并且放入這個(gè)子線程的消息隊(duì)列中去。當(dāng)這個(gè)子線程的消息循環(huán)處理這個(gè)消息的時(shí)候,就會(huì)調(diào)用這個(gè)LoaderTask對(duì)象的run函數(shù),因此,我們就可以在LoaderTask對(duì)象的run函數(shù)中通過調(diào)用loadAndBindAllApps來執(zhí)行加載系統(tǒng)中已經(jīng)安裝的應(yīng)用程序的任務(wù)了。

           

                  了解了HanderThread類的使用方法之后,我們就可以重點(diǎn)地來分析它的實(shí)現(xiàn)了:

           

          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類,因此,通過它可以在應(yīng)用程序中創(chuàng)建一個(gè)子線程,其次我們看到在它的run函數(shù)中,會(huì)進(jìn)入一個(gè)消息循環(huán)中,因此,這個(gè)子線程可以常駐在應(yīng)用程序中,直到它接收收到一個(gè)退出消息為止。

           

                  在run函數(shù)中,首先是調(diào)用Looper類的靜態(tài)成員函數(shù)prepare來準(zhǔn)備一個(gè)消息循環(huán)對(duì)象:

           

          Looper.prepare();
                  然后通過Looper類的myLooper成員函數(shù)將這個(gè)子線程中的消息循環(huán)對(duì)象保存在HandlerThread類中的成員變量mLooper中:

           

           

          mLooper = Looper.myLooper();
                  這樣,其它地方就可以方便地通過它的getLooper函數(shù)來獲得這個(gè)消息循環(huán)對(duì)象了,有了這個(gè)消息循環(huán)對(duì)象后,就可以往這個(gè)子線程的消息隊(duì)列中發(fā)送消息,通知這個(gè)子線程執(zhí)行特定的任務(wù)了。

           

                  最在這個(gè)run函數(shù)通過Looper類的loop函數(shù)進(jìn)入消息循環(huán)中:

           

          Looper.loop();
                  這樣,一個(gè)具有消息循環(huán)的應(yīng)用程序子線程就準(zhǔn)備就緒了。

           

                  HandlerThread類的實(shí)現(xiàn)雖然非常簡單,當(dāng)然這得益于Java提供的Thread類和Android自己本身提供的Looper類,但是它的想法卻非常周到,為應(yīng)用程序開發(fā)人員提供了很大的方便。
                  3. 需要與UI交互的應(yīng)用程序子線程消息模型

                  前面說過,我們開發(fā)應(yīng)用程序的時(shí)候,經(jīng)常中需要?jiǎng)?chuàng)建一個(gè)子線程來在后臺(tái)執(zhí)行一個(gè)特定的計(jì)算任務(wù),而在這個(gè)任務(wù)計(jì)算的過程中,需要不斷地將計(jì)算進(jìn)度或者計(jì)算結(jié)果展現(xiàn)在應(yīng)用程序的界面中。典型的例子是從網(wǎng)上下載文件,為了不阻塞應(yīng)用程序的主線程,我們開辟一個(gè)子線程來執(zhí)行下載任務(wù),子線程在下載的同時(shí)不斷地將下載進(jìn)度在應(yīng)用程序界面上顯示出來,這樣做出來程序就非常友好。由于子線程不能直接操作應(yīng)用程序的UI,因此,這時(shí)候,我們就可以通過往應(yīng)用程序的主線程中發(fā)送消息來通知應(yīng)用程序主線程更新界面上的下載進(jìn)度。因?yàn)轭愃频倪@種情景在實(shí)際開發(fā)中經(jīng)常碰到,Android系統(tǒng)為開發(fā)人員提供了一個(gè)異步任務(wù)類(AsyncTask)來實(shí)現(xiàn)上面所說的功能,即它會(huì)在一個(gè)子線程中執(zhí)行計(jì)算任務(wù),同時(shí)通過主線程的消息循環(huán)來獲得更新應(yīng)用程序界面的機(jī)會(huì)。

                  為了更好地分析AsyncTask的實(shí)現(xiàn),我們先舉一個(gè)例子來說明它的用法。在前面一篇文章Android系統(tǒng)中的廣播(Broadcast)機(jī)制簡要介紹和學(xué)習(xí)計(jì)劃中,我們開發(fā)了一個(gè)應(yīng)用程序Broadcast,其中使用了AsyncTask來在一個(gè)線程在后臺(tái)在執(zhí)行計(jì)數(shù)任務(wù),計(jì)數(shù)過程通過廣播(Broadcast)來將中間結(jié)果在應(yīng)用程序界面上顯示出來。在這個(gè)例子中,使用廣播來在應(yīng)用程序主線程和子線程中傳遞數(shù)據(jù)不是最優(yōu)的方法,當(dāng)時(shí)只是為了分析Android系統(tǒng)的廣播機(jī)制而有意為之的。在本節(jié)內(nèi)容中,我們稍微這個(gè)例子作一個(gè)簡單的修改,就可以通過消息的方式來將計(jì)數(shù)過程的中間結(jié)果在應(yīng)用程序界面上顯示出來。

                  為了區(qū)別Android系統(tǒng)中的廣播(Broadcast)機(jī)制簡要介紹和學(xué)習(xí)計(jì)劃一文中使用的應(yīng)用程序Broadcast,我們將本節(jié)中使用的應(yīng)用程序命名為Counter。首先在Android源代碼工程中創(chuàng)建一個(gè)Android應(yīng)用程序工程,名字就為Counter,放在packages/experimental目錄下。關(guān)于如何獲得Android源代碼工程,請(qǐng)參考在Ubuntu上下載、編譯和安裝Android最新源代碼一文;關(guān)于如何在Android源代碼工程中創(chuàng)建應(yīng)用程序工程,請(qǐng)參考在Ubuntu上為Android系統(tǒng)內(nèi)置Java應(yīng)用程序測試Application Frameworks層的硬件服務(wù)一文。這個(gè)應(yīng)用程序工程定義了一個(gè)名為shy.luo.counter的package,這個(gè)例子的源代碼主要就是實(shí)現(xiàn)在這個(gè)目錄下的Counter.java文件中:

           

          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);
          		}
          	};
          }
                  這個(gè)計(jì)數(shù)器程序很簡單,它在界面上有兩個(gè)按鈕Start和Stop。點(diǎn)擊Start按鈕時(shí),便會(huì)創(chuàng)建一個(gè)CounterTask實(shí)例task,然后調(diào)用它的execute函數(shù)就可以在應(yīng)用程序中啟動(dòng)一個(gè)子線程,并且通過調(diào)用這個(gè)CounterTask類的doInBackground函數(shù)來執(zhí)行計(jì)數(shù)任務(wù)。在計(jì)數(shù)的過程中,會(huì)通過調(diào)用publishProgress函數(shù)來將中間結(jié)果傳遞到onProgressUpdate函數(shù)中去,在onProgressUpdate函數(shù)中,就可以把中間結(jié)果顯示在應(yīng)用程序界面了。點(diǎn)擊Stop按鈕時(shí),便會(huì)通過設(shè)置變量stop為true,這樣,CounterTask類的doInBackground函數(shù)便會(huì)退出循環(huán),然后將結(jié)果返回到onPostExecute函數(shù)中去,在onPostExecute函數(shù),會(huì)把最終計(jì)數(shù)結(jié)果顯示在用程序界面中。

           

                 在這個(gè)例子中,我們需要注意的是:

                 A. CounterTask類繼承于AsyncTask類,因此它也是一個(gè)異步任務(wù)類;

                 B. CounterTask類的doInBackground函數(shù)是在后臺(tái)的子線程中運(yùn)行的,這時(shí)候它不可以操作應(yīng)用程序的界面;

                 C. CounterTask類的onProgressUpdate和onPostExecute兩個(gè)函數(shù)是應(yīng)用程序的主線程中執(zhí)行,它們可以操作應(yīng)用程序的界面。

                 關(guān)于C這一點(diǎn)的實(shí)現(xiàn)原理,我們?cè)诤竺鏁?huì)分析到,這里我們先完整地介紹這個(gè)例子,以便讀者可以參考做一下實(shí)驗(yàn)。

                 接下來我們?cè)倏纯磻?yīng)用程序的配置文件AndroidManifest.xml:

           

          <?xml version="1.0" encoding="utf-8"?>
          <manifest xmlns:android="http://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>
                 這個(gè)配置文件很簡單,我們就不介紹了。

           

                 再來看應(yīng)用程序的界面文件,它定義在res/layout/main.xml文件中:

           

          <?xml version="1.0" encoding="utf-8"?>  
          <LinearLayout xmlns:android="http://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>  
                 這個(gè)界面配置文件也很簡單,等一下我們?cè)谀M器把這個(gè)應(yīng)用程序啟動(dòng)起來后,就可以看到它的截圖了。

           

                 應(yīng)用程序用到的字符串資源文件位于res/values/strings.xml文件中:

           

          <?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> 
                 最后,我們還要在工程目錄下放置一個(gè)編譯腳本文件Android.mk:

           

           

          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)  
                 接下來就要編譯了。有關(guān)如何單獨(dú)編譯Android源代碼工程的模塊,以及如何打包system.img,請(qǐng)參考如何單獨(dú)編譯Android源代碼中的模塊一文。
                 執(zhí)行以下命令進(jìn)行編譯和打包:

           

           

          USER-NAME@MACHINE-NAME:~/Android$ mmm packages/experimental/Counter          
          USER-NAME@MACHINE-NAME:~/Android$ make snod
                 這樣,打包好的Android系統(tǒng)鏡像文件system.img就包含我們前面創(chuàng)建的Counter應(yīng)用程序了。
                 再接下來,就是運(yùn)行模擬器來運(yùn)行我們的例子了。關(guān)于如何在Android源代碼工程中運(yùn)行模擬器,請(qǐng)參考在Ubuntu上下載、編譯和安裝Android最新源代碼一文。
                 執(zhí)行以下命令啟動(dòng)模擬器:
          USER-NAME@MACHINE-NAME:~/Android$ emulator 
                 最后我們就可以在Launcher中找到Counter應(yīng)用程序圖標(biāo),把它啟動(dòng)起來,點(diǎn)擊Start按鈕,就會(huì)看到應(yīng)用程序界面上的計(jì)數(shù)器跑起來了:

           


                  這樣,使用AsyncTask的例子就介紹完了,下面,我們就要根據(jù)上面對(duì)AsyncTask的使用情況來重點(diǎn)分析它的實(shí)現(xiàn)了。

                  AsyncTask類定義在frameworks/base/core/java/android/os/AsyncTask.java文件中:

           

          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的實(shí)現(xiàn)可以看出,當(dāng)我們第一次創(chuàng)建一個(gè)AsyncTask對(duì)象時(shí),首先會(huì)執(zhí)行下面靜態(tài)初始化代碼創(chuàng)建一個(gè)線程池sExecutor:

           

          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提供的多線程機(jī)制之一,這里用的構(gòu)造函數(shù)原型為:

           

           

          ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, 
              BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory)
                  各個(gè)參數(shù)的意義如下:

           

                  corePoolSize -- 線程池的核心線程數(shù)量

                  maximumPoolSize -- 線程池的最大線程數(shù)量

                  keepAliveTime -- 若線程池的線程數(shù)數(shù)量大于核心線程數(shù)量,那么空閑時(shí)間超過keepAliveTime的線程將被回收

                  unit -- 參數(shù)keepAliveTime使用的時(shí)間單位

                  workerQueue -- 工作任務(wù)隊(duì)列

                  threadFactory -- 用來創(chuàng)建線程池中的線程
                  簡單來說,ThreadPoolExecutor的運(yùn)行機(jī)制是這樣的:每一個(gè)工作任務(wù)用一個(gè)Runnable對(duì)象來表示,當(dāng)我們要把一個(gè)工作任務(wù)交給這個(gè)線程池來執(zhí)行的時(shí)候,就通過調(diào)用ThreadPoolExecutor的execute函數(shù)來把這個(gè)工作任務(wù)加入到線程池中去。此時(shí),如果線程池中的線程數(shù)量小于corePoolSize,那么就會(huì)調(diào)用threadFactory接口來創(chuàng)建一個(gè)新的線程并且加入到線程池中去,再執(zhí)行這個(gè)工作任務(wù);如果線程池中的線程數(shù)量等于corePoolSize,但是工作任務(wù)隊(duì)列workerQueue未滿,則把這個(gè)工作任務(wù)加入到工作任務(wù)隊(duì)列中去等待執(zhí)行;如果線程池中的線程數(shù)量大于corePoolSize,但是小于maximumPoolSize,并且工作任務(wù)隊(duì)列workerQueue已經(jīng)滿了,那么就會(huì)調(diào)用threadFactory接口來創(chuàng)建一個(gè)新的線程并且加入到線程池中去,再執(zhí)行這個(gè)工作任務(wù);如果線程池中的線程量已經(jīng)等于maximumPoolSize了,并且工作任務(wù)隊(duì)列workerQueue也已經(jīng)滿了,這個(gè)工作任務(wù)就被拒絕執(zhí)行了。

                  創(chuàng)建好了線程池后,再創(chuàng)建一個(gè)消息處理器:

           

          private static final InternalHandler sHandler = new InternalHandler();
                  注意,這行代碼是在應(yīng)用程序的主線程中執(zhí)行的,因此,這個(gè)消息處理器sHandler內(nèi)部引用的消息循環(huán)對(duì)象looper是應(yīng)用程序主線程的消息循環(huán)對(duì)象,消息處理器的實(shí)現(xiàn)機(jī)制具體可以參考前面一篇文章Android應(yīng)用程序消息處理機(jī)制(Looper、Handler)分析

           

                  AsyncTask類的靜態(tài)初始化代碼執(zhí)行完成之后,才開始創(chuàng)建AsyncTask對(duì)象,即執(zhí)行AsyncTask類的構(gòu)造函數(shù):

           

          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類的構(gòu)造函數(shù)里面,主要是創(chuàng)建了兩個(gè)對(duì)象,分別是一個(gè)WorkerRunnable對(duì)象mWorker和一個(gè)FutureTask對(duì)象mFuture。

           

                  WorkerRunnable類實(shí)現(xiàn)了Runnable接口,此外,它的內(nèi)部成員變量mParams用于保存從AsyncTask對(duì)象的execute函數(shù)傳進(jìn)來的參數(shù)列表:

           

          private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
          	Params[] mParams;
          }
                  FutureTask類也實(shí)現(xiàn)了Runnable接口,所以它可以作為一個(gè)工作任務(wù)通過調(diào)用AsyncTask類的execute函數(shù)添加到sExecuto線程池中去:

           

           

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

           

                 這里的FutureTask對(duì)象mFuture是用來封裝前面的WorkerRunnable對(duì)象mWorker。當(dāng)mFuture加入到線程池中執(zhí)行時(shí),它調(diào)用的是mWorker對(duì)象的call函數(shù):

           

          mWorker = new WorkerRunnable<Params, Result>() {
          	public Result call() throws Exception {
          	       ......
          	       return doInBackground(mParams);
                  }
          };
                  在call函數(shù)里面,會(huì)調(diào)用AsyncTask類的doInBackground函數(shù)來執(zhí)行真正的任務(wù),這個(gè)函數(shù)是要由AsyncTask的子類來實(shí)現(xiàn)的,注意,這個(gè)函數(shù)是在應(yīng)用程序的子線程中執(zhí)行的,它不可以操作應(yīng)用程序的界面。

           

                  我們可以通過mFuture對(duì)象來操作當(dāng)前執(zhí)行的任務(wù),例如查詢當(dāng)前任務(wù)的狀態(tài),它是正在執(zhí)行中,還是完成了,還是被取消了,如果是完成了,還可以通過它獲得任務(wù)的執(zhí)行結(jié)果,如果還沒有完成,可以取消任務(wù)的執(zhí)行。

                  當(dāng)工作任務(wù)mWorker執(zhí)行完成的時(shí)候,mFuture對(duì)象中的done函數(shù)就會(huì)被被調(diào)用,根據(jù)任務(wù)的完成狀況,執(zhí)行相應(yīng)的操作,例如,如果是因?yàn)楫惓6瓿蓵r(shí),就會(huì)拋異常,如果是正常完成,就會(huì)把任務(wù)執(zhí)行結(jié)果封裝成一個(gè)AsyncTaskResult對(duì)象:

           

          private static class AsyncTaskResult<Data> {
          	final AsyncTask mTask;
          	final Data[] mData;
          
          	AsyncTaskResult(AsyncTask task, Data... data) {
          		mTask = task;
          		mData = data;
          	}
          }
                  其中,成員變量mData保存的是任務(wù)執(zhí)行結(jié)果,而成員變量mTask指向前面我們創(chuàng)建的AsyncTask對(duì)象。
                  最后把這個(gè)AsyncTaskResult對(duì)象封裝成一個(gè)消息,并且通過消息處理器sHandler加入到應(yīng)用程序主線程的消息隊(duì)列中:

           

           

          message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
          	new AsyncTaskResult<Result>(AsyncTask.this, result));
          message.sendToTarget();
                  這個(gè)消息最終就會(huì)在InternalHandler類的handleMessage函數(shù)中處理了:

           

           

          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;
          		......
          		}
          	}
          }
                  在這個(gè)函數(shù)里面,最終會(huì)調(diào)用前面創(chuàng)建的這個(gè)AsyncTask對(duì)象的finish函數(shù)來進(jìn)一步處理:

           

           

          private void finish(Result result) {
                 ......
                 onPostExecute(result);
                 ......
          }
                  這個(gè)函數(shù)調(diào)用AsyncTask類的onPostExecute函數(shù)來進(jìn)一步處理,AsyncTask類的onPostExecute函數(shù)一般是要由其子類來重載的,注意,這個(gè)函數(shù)是在應(yīng)用程序的主線程中執(zhí)行的,因此,它可以操作應(yīng)用程序的界面。
                  在任務(wù)執(zhí)行的過程當(dāng)中,即執(zhí)行doInBackground函數(shù)時(shí)候,可能通過調(diào)用publishProgress函數(shù)來將中間結(jié)果封裝成一個(gè)消息發(fā)送到應(yīng)用程序主線程中的消息隊(duì)列中去:

           

           

          protected final void publishProgress(Progress... values) {
          	sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
          		new AsyncTaskResult<Progress>(this, values)).sendToTarget();
          }
                  這個(gè)消息最終也是由InternalHandler類的handleMessage函數(shù)來處理的:

           

           

          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;
          		......
          		}
          	}
          }
                  這里它調(diào)用前面創(chuàng)建的AsyncTask對(duì)象的onPorgressUpdate函數(shù)來進(jìn)一步處理,這個(gè)函數(shù)一般是由AsyncTask的子類來實(shí)現(xiàn)的,注意,這個(gè)函數(shù)是在應(yīng)用程序的主線程中執(zhí)行的,因此,它和前面的onPostExecute函數(shù)一樣,可以操作應(yīng)用程序的界面。

           

                 這樣,AsyncTask類的主要實(shí)現(xiàn)就介紹完了,結(jié)合前面開發(fā)的應(yīng)用程序Counter來分析,會(huì)更好地理解它的實(shí)現(xiàn)原理。

                 至此,Android應(yīng)用程序線程消息循環(huán)模型就分析完成了,理解它有利于我們?cè)陂_發(fā)Android應(yīng)用程序時(shí),能夠充分利用多線程的并發(fā)性來提高應(yīng)用程序的性能以及獲得良好的用戶體驗(yàn)。

          作者:Luoshengyang 發(fā)表于2011-10-31 1:00:01 原文鏈接
          閱讀:4335 評(píng)論:20 查看評(píng)論
          posted on 2012-04-17 21:32 mixer-a 閱讀(330) 評(píng)論(0)  編輯  收藏

          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 南昌市| 盘锦市| 普定县| 安康市| 池州市| 偃师市| 隆化县| 寿阳县| 南丹县| 民勤县| 秀山| 文登市| 镇平县| 伊通| 马边| 宝鸡市| 绵竹市| 南乐县| 奉化市| 遂昌县| 英超| 广安市| 利川市| 吉安县| 报价| 武鸣县| 喀喇| 浏阳市| 阳泉市| 永丰县| 咸阳市| 高阳县| 桐城市| 平顶山市| 阜新市| 筠连县| 毕节市| 天祝| 太谷县| 康保县| 搜索|