【Android】IntentService & HandlerThread源碼解析

一、前言

  在學習Service的時候,我們一定會知道IntentService:官方文檔不止一次強調,Service本身是運行在主線程中的(詳見:【Android】Service),而主線程中是不適合進行耗時任務的,因而官方文檔叮囑我們一定要在Service中另開線程進行耗時任務處理。IntentService正是爲這個目的而誕生的一個優雅設計,讓程序員不用再管理線程的開啓和允許。

  至於介紹HandlerThread,一方面是因爲IntentService的實現中使用到了HandlerThread,另一方面是因爲IntentService和HandlerThread以及很多Android中的類一樣,其實都是爲了方便某個目的,對最基本的類進行的一定的擴充,並且結構精巧,便於使用,很適合閱讀研究。

 

二、HandlerThread源碼

先來一段結結實實的完整源碼:

複製代碼
  1 /*
  2  * Copyright (C) 2006 The Android Open Source Project
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package android.os;
 18 
 19 /**
 20  * Handy class for starting a new thread that has a looper. The looper can then be 
 21  * used to create handler classes. Note that start() must still be called.
 22  */
 23 public class HandlerThread extends Thread {
 24     int mPriority;
 25     int mTid = -1;
 26     Looper mLooper;
 27 
 28     public HandlerThread(String name) {
 29         super(name);
 30         mPriority = Process.THREAD_PRIORITY_DEFAULT;
 31     }
 32     
 33     /**
 34      * Constructs a HandlerThread.
 35      * @param name
 36      * @param priority The priority to run the thread at. The value supplied must be from 
 37      * {@link android.os.Process} and not from java.lang.Thread.
 38      */
 39     public HandlerThread(String name, int priority) {
 40         super(name);
 41         mPriority = priority;
 42     }
 43     
 44     /**
 45      * Call back method that can be explicitly overridden if needed to execute some
 46      * setup before Looper loops.
 47      */
 48     protected void onLooperPrepared() {
 49     }
 50 
 51     @Override
 52     public void run() {
 53         mTid = Process.myTid();
 54         Looper.prepare();
 55         synchronized (this) {
 56             mLooper = Looper.myLooper();
 57             notifyAll();
 58         }
 59         Process.setThreadPriority(mPriority);
 60         onLooperPrepared();
 61         Looper.loop();
 62         mTid = -1;
 63     }
 64     
 65     /**
 66      * This method returns the Looper associated with this thread. If this thread not been started
 67      * or for any reason is isAlive() returns false, this method will return null. If this thread 
 68      * has been started, this method will block until the looper has been initialized.  
 69      * @return The looper.
 70      */
 71     public Looper getLooper() {
 72         if (!isAlive()) {
 73             return null;
 74         }
 75         
 76         // If the thread has been started, wait until the looper has been created.
 77         synchronized (this) {
 78             while (isAlive() && mLooper == null) {
 79                 try {
 80                     wait();
 81                 } catch (InterruptedException e) {
 82                 }
 83             }
 84         }
 85         return mLooper;
 86     }
 87 
 88     /**
 89      * Quits the handler thread's looper.
 90      * <p>
 91      * Causes the handler thread's looper to terminate without processing any
 92      * more messages in the message queue.
 93      * </p><p>
 94      * Any attempt to post messages to the queue after the looper is asked to quit will fail.
 95      * For example, the {@link Handler#sendMessage(Message)} method will return false.
 96      * </p><p class="note">
 97      * Using this method may be unsafe because some messages may not be delivered
 98      * before the looper terminates.  Consider using {@link #quitSafely} instead to ensure
 99      * that all pending work is completed in an orderly manner.
100      * </p>
101      *
102      * @return True if the looper looper has been asked to quit or false if the
103      * thread had not yet started running.
104      *
105      * @see #quitSafely
106      */
107     public boolean quit() {
108         Looper looper = getLooper();
109         if (looper != null) {
110             looper.quit();
111             return true;
112         }
113         return false;
114     }
115 
116     /**
117      * Quits the handler thread's looper safely.
118      * <p>
119      * Causes the handler thread's looper to terminate as soon as all remaining messages
120      * in the message queue that are already due to be delivered have been handled.
121      * Pending delayed messages with due times in the future will not be delivered.
122      * </p><p>
123      * Any attempt to post messages to the queue after the looper is asked to quit will fail.
124      * For example, the {@link Handler#sendMessage(Message)} method will return false.
125      * </p><p>
126      * If the thread has not been started or has finished (that is if
127      * {@link #getLooper} returns null), then false is returned.
128      * Otherwise the looper is asked to quit and true is returned.
129      * </p>
130      *
131      * @return True if the looper looper has been asked to quit or false if the
132      * thread had not yet started running.
133      */
134     public boolean quitSafely() {
135         Looper looper = getLooper();
136         if (looper != null) {
137             looper.quitSafely();
138             return true;
139         }
140         return false;
141     }
142 
143     /**
144      * Returns the identifier of this thread. See Process.myTid().
145      */
146     public int getThreadId() {
147         return mTid;
148     }
149 }
複製代碼

  總共就149行代碼。下面撿重點分析。

  首先,類註釋(20-21行)明確指出,該類實現了一個帶looper的Thread。23行明確看出HandlerThread是繼承於Thread的。那麼爲什麼需要一個Thread帶上looper呢?如果想要了解,可以閱讀:Android Handler機制,想要深入瞭解,則可以閱讀【Android】Handler、Looper源碼分析。簡而言之,一個類具有的Looper,就可以接受並且處理消息了。當我們不用HandlerThread而直接使用Thread去實現這樣一個功能的時候,需要如下代碼:

複製代碼
 1 class LooperThread extends Thread {
 2      public Handler mHandler;
 3 
 4      public void run() {
 5           Looper.prepare();
 6 
 7           mHandler = new Handler() {
 8              public void handleMessage(Message msg) {
 9                  // process incoming messages here
10               }
11          };
12          Looper.loop();
13      }
14 }
複製代碼

  至於其中的Looper.prepare()和Looper.loop()方法起什麼作用,讀完【Android】Handler、Looper源碼分析應該一目瞭然。OK,很明顯,這樣創建一個戴Handler的Thread有很多重複的地方,那麼怎麼複用這些代碼,讓程序員可以直接而簡單的創建呢?我們來看HandlerThread的實現。

  該類註釋很明確的說,使用之前必須調用start()方法,Thread類的start()方法調用後會去執行run()方法體,而源碼中51-63行則覆蓋了Thread的run()方法,注意到這裏實現了兩行最關鍵的代碼:54和61行。因而一個Thread就具備了Looper的特性。當然,裏面爲了讓用戶有足夠的可操控性,還設置了一個回調方法。

  另外,讀者可能對55-58行感到奇怪:爲什麼需要synchronized關鍵字修飾兩行代碼呢?答案其實在另外一個方法裏面:71-86行的getLooper()方法中,getLooper()的時候,線程可能已經啓動,但是還沒有準備好Looper,而從run()看,其實很快就會準備好,因而這個方法回去wait(),55-58行代碼的目的是:一旦準備好Looper,就立馬通知被阻塞的線程,防止有別的線程因爲調用自身的getLooper()方法而阻塞。

  OK,HandlerThread就介紹到這裏。至於怎麼使用它,IntentService提供了完美的例子。

 

三、IntentService

同樣來一段結實的完整源碼:

複製代碼
  1 /*
  2  * Copyright (C) 2008 The Android Open Source Project
  3  *
  4  * Licensed under the Apache License, Version 2.0 (the "License");
  5  * you may not use this file except in compliance with the License.
  6  * You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 package android.app;
 18 
 19 import android.content.Intent;
 20 import android.os.Handler;
 21 import android.os.HandlerThread;
 22 import android.os.IBinder;
 23 import android.os.Looper;
 24 import android.os.Message;
 25 
 26 /**
 27  * IntentService is a base class for {@link Service}s that handle asynchronous
 28  * requests (expressed as {@link Intent}s) on demand.  Clients send requests
 29  * through {@link android.content.Context#startService(Intent)} calls; the
 30  * service is started as needed, handles each Intent in turn using a worker
 31  * thread, and stops itself when it runs out of work.
 32  *
 33  * <p>This "work queue processor" pattern is commonly used to offload tasks
 34  * from an application's main thread.  The IntentService class exists to
 35  * simplify this pattern and take care of the mechanics.  To use it, extend
 36  * IntentService and implement {@link #onHandleIntent(Intent)}.  IntentService
 37  * will receive the Intents, launch a worker thread, and stop the service as
 38  * appropriate.
 39  *
 40  * <p>All requests are handled on a single worker thread -- they may take as
 41  * long as necessary (and will not block the application's main loop), but
 42  * only one request will be processed at a time.
 43  *
 44  * <div class="special reference">
 45  * <h3>Developer Guides</h3>
 46  * <p>For a detailed discussion about how to create services, read the
 47  * <a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a> developer guide.</p>
 48  * </div>
 49  *
 50  * @see android.os.AsyncTask
 51  */
 52 public abstract class IntentService extends Service {
 53     private volatile Looper mServiceLooper;
 54     private volatile ServiceHandler mServiceHandler;
 55     private String mName;
 56     private boolean mRedelivery;
 57 
 58     private final class ServiceHandler extends Handler {
 59         public ServiceHandler(Looper looper) {
 60             super(looper);
 61         }
 62 
 63         @Override
 64         public void handleMessage(Message msg) {
 65             onHandleIntent((Intent)msg.obj);
 66             stopSelf(msg.arg1);
 67         }
 68     }
 69 
 70     /**
 71      * Creates an IntentService.  Invoked by your subclass's constructor.
 72      *
 73      * @param name Used to name the worker thread, important only for debugging.
 74      */
 75     public IntentService(String name) {
 76         super();
 77         mName = name;
 78     }
 79 
 80     /**
 81      * Sets intent redelivery preferences.  Usually called from the constructor
 82      * with your preferred semantics.
 83      *
 84      * <p>If enabled is true,
 85      * {@link #onStartCommand(Intent, int, int)} will return
 86      * {@link Service#START_REDELIVER_INTENT}, so if this process dies before
 87      * {@link #onHandleIntent(Intent)} returns, the process will be restarted
 88      * and the intent redelivered.  If multiple Intents have been sent, only
 89      * the most recent one is guaranteed to be redelivered.
 90      *
 91      * <p>If enabled is false (the default),
 92      * {@link #onStartCommand(Intent, int, int)} will return
 93      * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
 94      * dies along with it.
 95      */
 96     public void setIntentRedelivery(boolean enabled) {
 97         mRedelivery = enabled;
 98     }
 99 
100     @Override
101     public void onCreate() {
102         // TODO: It would be nice to have an option to hold a partial wakelock
103         // during processing, and to have a static startService(Context, Intent)
104         // method that would launch the service & hand off a wakelock.
105 
106         super.onCreate();
107         HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
108         thread.start();
109 
110         mServiceLooper = thread.getLooper();
111         mServiceHandler = new ServiceHandler(mServiceLooper);
112     }
113 
114     @Override
115     public void onStart(Intent intent, int startId) {
116         Message msg = mServiceHandler.obtainMessage();
117         msg.arg1 = startId;
118         msg.obj = intent;
119         mServiceHandler.sendMessage(msg);
120     }
121 
122     /**
123      * You should not override this method for your IntentService. Instead,
124      * override {@link #onHandleIntent}, which the system calls when the IntentService
125      * receives a start request.
126      * @see android.app.Service#onStartCommand
127      */
128     @Override
129     public int onStartCommand(Intent intent, int flags, int startId) {
130         onStart(intent, startId);
131         return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
132     }
133 
134     @Override
135     public void onDestroy() {
136         mServiceLooper.quit();
137     }
138 
139     /**
140      * Unless you provide binding for your service, you don't need to implement this
141      * method, because the default implementation returns null. 
142      * @see android.app.Service#onBind
143      */
144     @Override
145     public IBinder onBind(Intent intent) {
146         return null;
147     }
148 
149     /**
150      * This method is invoked on the worker thread with a request to process.
151      * Only one Intent is processed at a time, but the processing happens on a
152      * worker thread that runs independently from other application logic.
153      * So, if this code takes a long time, it will hold up other requests to
154      * the same IntentService, but it will not hold up anything else.
155      * When all requests have been handled, the IntentService stops itself,
156      * so you should not call {@link #stopSelf}.
157      *
158      * @param intent The value passed to {@link
159      *               android.content.Context#startService(Intent)}.
160      */
161     protected abstract void onHandleIntent(Intent intent);
162 }
複製代碼

  同樣很短,只有162行。IntentService的目的前面已經敘述了,這邊類的註釋中也有描述。IntentService實現了"work queue processor",可以將任務剝離主線程(即不會阻塞主線程)並按次序完成任務,當任務完成之後,則會自動關閉自身~聽起來非常神奇,非常方便,那如何實現呢?

  我們知道一個Service的聲明週期如下:

  

  這兩列分別表示兩種啓動Service的方法:startService和bindService。源碼144-147行的實現否決了右邊調用bindService啓動Service的方法(不是不可用,程序員可以重載IntentService進行進一步的定製,只不過直接使用IntentService的bindService是不能沒有意義的~)。既然如此,我們按照左邊的生命週期查看源碼。

  首先看100-112行的OnCreate()方法,看到沒有?HandlerThread!源碼在這裏創建了一個HandlerThread,HandlerThread的使用方法就在107-111行處~首先是創建實例,然後必須start()(想想run()方法裏面幹了什麼?),接着通過getLooper方法取出Looper用於第111行的ServiceHandler創建,這是一個內部類,繼承了Handler而已,重載handleMessage方法,執行兩個動作:回調onHandleIntent方法,終止Service。

  128-132行重載了onStartCommand方法,這個方法每次在startService()方法調用的時候都會被執行,它可以根據一個boolean值決定返回值(這裏可以去查看一下Service該方法返回值的含義,它決定了Service被殺死之後如何復甦),另外也調用了onStart()方法,即執行114-120行的代碼。onStart方法則將傳入的intent以及startId包裝秤一個msg,交給mServiceHandler發送給HandlerThread的實例去處理。

  OKOK,到這裏一口氣吃的有點多,停下來整理一下~IntentService到底幹了什麼?IntentService在內部啓動了一個帶Looper的Thread,當然,這個Thread也就具備類消息處理的能力,外部每一次調用startService,都會傳進一個Intent,而該Intent稍後就會被封裝成消息交給HandlerThread處理,處理完畢之後,HandlerThread會主動調用stopSelf()停止服務。

  不知道有沒有讀者感到奇怪,這裏都已經停止服務了,怎麼還能繼續處理消息呢?其實Service在這裏只是主線程和工作線程之間的一個橋樑,Service的Intance唯一性保證了不管調用幾次startService,只會有一個工作線程被實例化,從而接受工作,HandlerThread其實是一個一直在等待消息的工作線程,而Service只是負責將任務封裝成消息交給它,而每次要傳遞任務,都必須調用IntentService的startService()方法啓動Service,因而在任務執行完畢後關閉Service是沒有問題,不會影響後續的任務傳遞,但是如果任務正在傳遞中,比如新的任務傳遞執行到117行,前一個任務剛好執行完畢,這個時候調用stopSelf,即66行,會發生什麼呢?官方文檔有解釋:

However, if your service handles multiple requests to onStartCommand() concurrently, then you shouldn't stop the service when you're done processing a start request, because you might have since received a new start request (stopping at the end of the first request would terminate the second one). To avoid this problem, you can use stopSelf(int) to ensure that your request to stop the service is always based on the most recent start request. That is, when you call stopSelf(int), you pass the ID of the start request (the startId delivered to onStartCommand()) to which your stop request corresponds. Then if the service received a new start request before you were able to call stopSelf(int), then the ID will not match and the service will not stop.

  OK,到這裏,關於IntentService的實現也解釋完畢了。

 

四、總結

  Android中有很多這樣短小精悍的代碼,以精妙的設計方式簡化了開發過程,非常值得學習,IntentService的實現展現了內部類的作用,內部類和外部類的交互顯得自然而又緊密,非常NICE,很值得學習研究~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章