本人只是 Android小菜一個,寫技術文檔只是爲了總結本身在最近學習到的知識,歷來不敢爲人師,若是裏面有些不正確的地方請你們盡情指出,謝謝!java
service
的做用相信你們都是很是熟悉的,主要用來在後臺進行任務處理,例如後臺播放音樂、下載文件、上傳文件等等。因爲service
是運行在主線程中的,也有必定的時間限制,若是在主線程中對一個任務的處理時間超過了限制,進程就會出現「應用不響應」,即ANR, Application Not Responding
。爲了不這樣狀況,都會在service
裏用新的thread
處理一些可能須要更多處理時間的任務。android
其實Android
早就替咱們設計了一種更方便的service + thread
模式,就是本文要講的IntentService
,經過它能夠很方便地實如今service
中使用thread
進行耗時任務的處理。express
本文將首先給你們演示下它的基本使用方式,再講解下IntentService
的內部原理。bash
在知道如何使用前,先看看IntentService
究竟是什麼東西,它的聲明以下:app
/** * IntentService is a base class for {@link Service}s that handle asynchronous * requests (expressed as {@link Intent}s) on demand. Clients send requests * through {@link android.content.Context#startService(Intent)} calls; the * service is started as needed, handles each Intent in turn using a worker * thread, and stops itself when it runs out of work. * * <p>This "work queue processor" pattern is commonly used to offload tasks * from an application's main thread. The IntentService class exists to * simplify this pattern and take care of the mechanics. To use it, extend * IntentService and implement {@link #onHandleIntent(Intent)}. IntentService * will receive the Intents, launch a worker thread, and stop the service as * appropriate. * * <p>All requests are handled on a single worker thread -- they may take as * long as necessary (and will not block the application's main loop), but * only one request will be processed at a time. */
public abstract class IntentService extends Service { ... }
複製代碼
相信你們都能很容易看懂這段聲明的意思,小菜在這裏簡單爲你們總結下,這麼一大段文字主要是說明了兩個問題:異步
IntentService
是什麼:用來進行處理異步請求的服務,其內部有一個工做線程,全部發送給服務的請求都會在這個工做線程中按序執行,在處理完全部請求後服務會自動中止。IntentService
如何使用:拓展IntentService
並在其拓展類或者叫子類中實現onHandleIntent(Intent)
接口,在這個接口中進行實際的請求處理,這些請求經過Context.startService(Intent)
來進行發送。
Android SDK
真的能夠做爲全部SDK
的典範,它會清楚地告訴你「是什麼」和「怎麼用」的問題,針對相對複雜的狀況,還會直接在聲明裏給出範例。async
既然咱們已經知道要如何使用IntentService
了,就讓咱們用一個小例子來演示一下:ide
服務端指的是IntentService
端,其做用是接收客戶端發送過來的請求並處理。函數
public class TestIntentService extends IntentService {
private static final String TAG = "TestIntentService";
private static final String DEFAULT_NAME = "default_name";
// 爲了區分不一樣的請求和方便調用端使用,直接定義了不一樣的 ACTION.
public static final String DOWNLOAD_ACTION = "com.test.intent.action.DOWNLOAD";
public static final String UPLOAD_ACTION = "com.test.intent.action.UOLOAD";
// 要在 AndroidManifest.xml 裏聲明 servcie,必須提供一個無參構造函數.
public TestIntentService() {
// IntentService 的構造函數須要提供一個工做線程的名字信息.
super(DEFAULT_NAME);
}
@Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy");
}
@Override
public void onHandleIntent(Intent intent) {
String action = intent.getAction();
// 根據不一樣的請求類型進行不一樣的處理,這裏只是休眠一段時間,並無進行實際的處理。
if (DOWNLOAD_ACTION.equals(action)) {
try {
Log.i(TAG, "onHandleIntent, start to download");
Thread.sleep(30 * 1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
} else if (UPLOAD_ACTION.equals(action)) {
try {
Log.i(TAG, "onHandleIntent, start to upload");
Thread.sleep(40 * 1000);
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
}
複製代碼
在這段代碼裏,請求處理函數onHandleIntent(Intent)
會根據接收到的請求進行不一樣的處理,若是收到的是「下載」請求就休眠30秒模擬下載過程,若是收到的是「上傳」請求就休眠40秒模擬上傳過程。oop
在寫好了service
邏輯後必定不要忘記在AndroidManifest.xml
對其進行註冊,不然是沒法使用的,註冊代碼以下:
<service android:name=".TestIntentService" />
複製代碼
這裏只是簡單地對其進行註冊,並無設置其餘相關屬性,例如
intent-filter
,由於這些和本文所講內容並沒有直接關係。
客戶端主要是用來向服務端發送請求。
// 發送「下載」請求
Intent downloadIntent = new Intent(this, TestIntentService.class);
downloadIntent.setAction(TestIntentService.DOWNLOAD_ACTION);
startService(downloadIntent);
// 發送「上傳」請求
Intent upIntent = new Intent(this, TestIntentService.class);
upIntent.setAction(TestIntentService.UPLOAD_ACTION);
startService(upIntent);
複製代碼
如今看當發送這「下載」和「上傳」請求後,IntentService
是如何響應的:
02-27 12:58:23.100 24190 24190 I TestIntentService: onCreate
02-27 12:58:23.102 24190 24240 I TestIntentService: onHandleIntent, start to download
02-27 12:58:53.107 24190 24240 I TestIntentService: onHandleIntent, start to upload
02-27 12:59:33.115 24190 24190 I TestIntentService: onDestroy
複製代碼
能夠看到:在發送「下載」請求的時候,service
首先被建立,而後開始處理這個「下載請求」,僅接着「上傳」請求也被接收並在處理完第一個請求後開始處理,在處理完全部請求後service
被自動銷燬。
前面已經講了如何經過IntentService
實如今工做線程中處理較耗時任務,那麼IntentService
內部又是如何實現的呢?本節咱們經過分析它的源碼來一探究竟。
既然IntentService
的功能是在工做線程中處理任務,首先來看看這個工做線程是如何建立出來的。
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
// 建立工做線程
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
// 和工做線程內部的消息循環關聯
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
複製代碼
當IntentService
第一次啓動的時候會調用其onCreate
來完成一些初始化操做:
HandlerThread
對象,這就是前面一直提到的「工做線程」。你們對Handler
和Thread
都很瞭解,那這個HandlerThread
是什麼呢?簡單來講,它就是內部有一個消息循環隊列的線程,咱們知道默認的線程內部是沒有消息循環隊列的,這就致使咱們沒法直接在其內部使用Handler
。Android
爲了方便使用,直接提供了一個含有消息循環隊列的HandlerThread
。HandlerThread
內部的消息循環建立一個 ServiceHandler
對象,這樣它的消息處理函數handleMessage
就會在對應的線程中執行了。既然工做線程已經建立完成,這時就要考慮如何接收和處理客戶端發送過來的請求了,已經瞭解到客戶端是經過startService
來發送請求的,結合service
的生命週期,會執行onStartCommand
回調:
/** * You should not override this method for your IntentService. Instead, * override {@link #onHandleIntent}, which the system calls when the IntentService * receives a start request. * @see android.app.Service#onStartCommand */
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
複製代碼
從這段代碼看到,onStartCommand
會直接調用onStart
,在這裏對發送過來的請求接收並經過mServiceHandler
進行處理。
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
複製代碼
在handleMessage
中對接收到的請求用onHandleIntent
進行實際處理,而onHandleIntent
就是咱們在使用過程當中必須實現的處理邏輯。
前面提到:當全部請求都被處理完成後,service
就會被銷燬,這是如何實現的呢?在上面看到handleMessage
方法裏在處理完當前請求時會調用stopSelf(msg.arg1)
來嘗試中止當前服務,之因此說「嘗試」,是由於它不必定能真正中止服務。仍是來看下stopSelf(int)
的實現代碼:
/** * Old version of {@link #stopSelfResult} that doesn't return a result. * * @see #stopSelfResult */
public final void stopSelf(int startId) {
if (mActivityManager == null) {
return;
}
try {
mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
}
/** * Stop the service if the most recent time it was started was * <var>startId</var>. This is the same as calling {@link * android.content.Context#stopService} for this particular service but allows you to * safely avoid stopping if there is a start request from a client that you * haven't yet seen in {@link #onStart}. */
public final boolean stopSelfResult(int startId) {
if (mActivityManager == null) {
return false;
}
try {
return mActivityManager.stopServiceToken(
new ComponentName(this, mClassName), mToken, startId);
} catch (RemoteException ex) {
}
return false;
}
複製代碼
在stopSelf(int)
的聲明裏提到它是stopSelfResult(int)
的老版本,惟一的區別就是沒有返回值。那咱們直接看stopSelfResult(int)
的聲明,其中提到只有在當前的service
的最近一次啓動是startId
發起的纔會被中止。咱們把這句話放在IntentService
的場景裏去理解,若是說當前接收到3個請求,在處理第一個請求後打算去中止服務,可是調用stopSelf(int)
的時候發現最後一次啓動是第三個請求發生的,並不會中止服務;處理完第二個請求後是相似的,只有在處理完第三個請求後,去嘗試中止服務,這時發現最近一次啓動就是它發起的,能夠去中止服務了。
中止服務時,其onDestroy
會獲得調用:
@Override
public void onDestroy() {
mServiceLooper.quit();
}
複製代碼
在這裏會中止工做線程的消息循環,等待線程退出。
IntentService
可以接受用戶發送的請求並在工做線程中順序處理,處理完成後自動退出,可是因爲從 Android O
開始對後臺服務增長了更嚴格的控制,致使當前進程在後臺時其含有的後臺服務也沒法長期存活,IntentService
的使用也有了必定的限制,推薦使用更好的JobIntentService
,感興趣的同窗能夠本身去研究。