Android中IntentService的使用及其原始碼解析
為什麼我們需要IntentService ?
Android中的IntentService是繼承自Service類的,在我們討論IntentService之前,我們先想一下Service的特點: Service的回撥方法(onCreate、onStartCommand、onBind、onDestroy)都是執行在主執行緒中的。當我們通過startService啟動Service之後,我們就需要在Service的onStartCommand方法中寫程式碼完成工作,但是onStartCommand是執行在主執行緒中的,如果我們需要在此處完成一些網路請求或IO等耗時操作,這樣就會阻塞主執行緒UI無響應,從而出現ANR現象。為了解決這種問題,最好的辦法就是在onStartCommand中建立一個新的執行緒,並把耗時程式碼放到這個新執行緒中執行。可以參考之前的文章《Android通過startService實現批量下載示例》,這篇文章在onStartCommand中開啟了新的執行緒作為工作執行緒去執行網路請求,所以這樣不會阻塞主執行緒。由此看來,建立一個帶有工作執行緒的Service是一種很常見的需求(因為工作執行緒不會阻塞主執行緒),所以Android為了簡化開發帶有工作執行緒的Service,Android額外開發了一個類——–IntentService。
IntentService的特點
IntentService具有以下特點:
1. IntentService自帶一個工作執行緒,當我們的Service需要做一些可能會阻塞主執行緒的工作的時候可以考慮使用IntentService。
2. 我們需要將要做的實際工作放入到IntentService的onHandleIntent回到方法中,當我們通過startService(intent)啟動了IntentService之後,最終Android Framework會回撥其onHandleIntent方法,並將intent傳入該方法,這樣我們就可以根據intent去做實際工作,並且onHandleIntent執行在IntentService所持有的工作執行緒中,而非主執行緒。
3. 當我們通過startService多次啟動了IntentService,這會產生多個job,由於IntentService只持有一個工作執行緒,所以每次onHandleIntent只能處理一個job。如果存在多個job,IntentService會如何處理?處理方式是one-by-one,也就是一個一個按照先後順序處理,先將intent1傳入onHandleIntent,讓其完成job1,然後將intent2傳入onHandleIntent,讓其完成job2…這樣直至所有job完成,所以我們IntentService不能並行地執行多個job,只能一個一個的按照先後順序完成,當所有job完成的時候IntentService就銷燬了,會執行onDestroy回撥方法。
如何使用IntentService ?
在《Android通過startService實現批量下載示例》一文中,我們演示瞭如何通過Service批量下載文章,現在在本文中我們還是要演示如何批量下載文章,只不過是用IntentService完成這項工作。
系統介面如下:
介面很簡單,就一個按鈕“批量下載文章”,通過該Activity上的按鈕啟動DownloadService。
DownloadService是用來進行下載CSDN上部落格文章的服務,程式碼如下:
package com.ispring.startservicedemo;
import android.app.IntentService;
import android.content.Intent;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class DownloadIntentService extends IntentService {
public DownloadIntentService(){
super("Download");
Log.i("DemoLog", "DownloadIntentService建構函式, Thread: " + Thread.currentThread().getName());
}
@Override
public void onCreate() {
super.onCreate();
Log.i("DemoLog", "DownloadIntentService -> onCreate, Thread: " + Thread.currentThread().getName());
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("DemoLog", "DownloadIntentService -> onStartCommand, Thread: " + Thread.currentThread().getName() + " , startId: " + startId);
return super.onStartCommand(intent, flags, startId);
}
@Override
protected void onHandleIntent(Intent intent) {
HttpURLConnection conn = null;
InputStream is = null;
String blogUrl = intent.getStringExtra("url");
String blogName = intent.getStringExtra("name");
try{
//下載指定的檔案
URL url = new URL(blogUrl);
conn = (HttpURLConnection)url.openConnection();
if(conn != null){
//我們在此處得到所下載文章的輸入流,可以將其以檔案的形式寫入到儲存卡上面或
//將其讀取出文字顯示在App中
is = conn.getInputStream();
}
}catch (MalformedURLException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}finally {
if(conn != null){
conn.disconnect();
}
if(is != null){
is.close();
}
}
Log.i("DemoLog", "DownloadIntentService -> onHandleIntent, Thread: " + Thread.currentThread().getName() + ", 《" + blogName + "》下載完成");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("DemoLog", "DownloadIntentService -> onDestroy, Thread: " + Thread.currentThread().getName());
}
}
DownloadActivity的程式碼如下:
package com.ispring.startservicedemo;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class DownloadActivity extends Activity implements Button.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_download);
}
@Override
public void onClick(View v) {
List<String> list = new ArrayList<>();
list.add("Android中Handler的使用;http://blog.csdn.net/iispring/article/details/47115879");
list.add("深入原始碼解析Android中的Handler,Message,MessageQueue,Looper;http://blog.csdn.net/iispring/article/details/47180325");
list.add("Android新執行緒中更新主執行緒UI中的View方法彙總;http://blog.csdn.net/iispring/article/details/47300819");
list.add("Android中HandlerThread的使用及原理解析;http://blog.csdn.net/iispring/article/details/47320407");
list.add("Android中Looper的quit方法和quitSafely方法;http://blog.csdn.net/iispring/article/details/47622705");
Iterator iterator = list.iterator();
while (iterator.hasNext()){
String str = (String)iterator.next();
String[] splits = str.split(";");
String name = splits[0];
String url = splits[1];
Intent intent = new Intent(this, DownloadIntentService.class);
intent.putExtra("name", name);
intent.putExtra("url", url);
//啟動IntentService
startService(intent);
}
}
}
當我們單擊了按鈕“批量下載文章”時,我們會多次呼叫Activity的startService方法,其中我們在其引數intent中儲存了文章名name以及文章的地址url,由於我們多次呼叫了startService方法,所以會批量下載文章。
點選按鈕後,控制檯執行結果如下所示:
通過以上的輸出結果我們可以發現,DownloadIntentService的onCreate、onStartCommand、onDestroy回撥方法都是執行在主執行緒中的,而onHandleIntent是執行在工作執行緒IntentService[Download]中的,這驗證了我們上面所說的IntentService的第一個和第二個特點。
通過上面的輸出結果我們還會發現,在我們連續呼叫了五次startService(intent)之後,onStartCommand依次被呼叫了五次,然後依次執行了onHandleIntent五次,這樣就依次完成了job,當最後一個job完成,也就是在最後一次onHandleIntent呼叫完成之後,整個IntentService的工作都完成,執行onDestroy回撥方法,IntentService銷燬。
IntentService工作原理及原始碼解析
在上面我們已經介紹了IntentService的特點以及如何使用,那麼你可能會疑問Android是如何將排程這些intent將其傳入onHandleIntent完成工作的,其實IntentService的工作原理很簡單,將intent轉換為Message並放到訊息佇列中,然後讓Handler依次從中取出Message對其進行處理。
IntentService的原始碼如下:
package android.app;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
//在工作執行緒中呼叫onHandleIntent,確保onHandleIntent不會阻塞主執行緒
onHandleIntent((Intent)msg.obj);
//在執行完了onHandleIntent之後,我們需要呼叫stopSelf(startId)宣告某個job完成了
//當所有job完成的時候,Android就會回撥onDestroy方法,銷燬IntentService
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
//此處的name將用作執行緒名稱
super();
mName = name;
}
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
//建立HandlerThread,利用mName作為執行緒名稱,HandlerThread是IntentService的工作執行緒
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
//將建立的HandlerThread所繫結的looper物件傳遞給ServiceHandler,
//這樣我們建立的Handler就和HandlerThread通過訊息佇列繫結在了一起
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(Intent intent, int startId) {
//在此方法中建立Message物件,並將intent作為Message的obj引數,
//這樣Message與Intent就關聯起來了
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
//將關聯了Intent資訊的Message傳送給Handler
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//IntentService重寫了onStartCommand回撥方法:在內部呼叫onStart回撥方法
//所以我們在繼承IntentService時,不應該再覆寫該方法,即便覆蓋該方法,我們也應該呼叫super.onStartCommand()
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
//在onDestroy方法中呼叫了Handler的quit方法,該方法會終止訊息迴圈
mServiceLooper.quit();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
protected abstract void onHandleIntent(Intent intent);
}
我對上面的程式碼已經加了很多註釋,相信大家直接看程式碼就能理解IntentService是如何運作的了。
IntentService繼承自Service類,並且IntentService重寫了onCreate、onStartCommand、onStart、onDestroy回撥方法,並且IntentService還新增了一個onHandleIntent回撥方法。下面我們依次解釋這幾個方法在IntentService的作用。
onCreate: 在onCreate回撥方法中,利用mName作為執行緒名稱,建立HandlerThread,HandlerThread是IntentService的工作執行緒。HandlerThread在執行了start方法之後,其本身就關聯了訊息佇列和Looper,並且訊息佇列開始迴圈起來。
onStartCommand: IntentService重寫了onStartCommand回撥方法:在內部呼叫onStart回撥方法。
onStart: 在onStart方法中建立Message物件,並將intent作為Message的obj引數,這樣Message與Intent就關聯起來了,然後通過Handler的sendMessage方法將關聯了Intent資訊的Message傳送給Handler。
onHandleIntent: 當在onStart方法中,通過sendMessage方法將Message放入到Handler所關聯的訊息佇列中後,Handler所關聯的Looper物件會從訊息佇列中取出一個Message,然後將其傳入Handler的handleMessage方法中,在handleMessage方法中首先通過Message的obj獲取到了原始的Intent物件,然後將其作為引數傳給了onHandleIntent方法讓其執行。handleMessage方法是執行在HandlerThread的,所以onHandleIntent也是執行在工作執行緒中的。在執行完了onHandleIntent之後,我們需要呼叫stopSelf(startId)宣告某個job完成了。當所有job完成的時候,Android就會回撥onDestroy方法,銷燬IntentService。
onDestroy: 當所有job完成的時候,Service會銷燬並執行其onDestroy回撥方法。在該方法中,呼叫了Handler的quit方法,該方法會終止訊息迴圈。
總結
IntentService可以在工作執行緒中完成工作而不阻塞主執行緒,但是IntentService不能並行處理多個job,只能依次處理,一個接一個,當所有的job完成後,會自動執行onDestroy方法而無需我們自己呼叫stopSelf()或stopSelf(startId)方法。IntentService並不神祕,只是Android對一種常見開發方式的封裝,便於開發人員減少開發工作量。 IntentService是個助手類,如果Android沒有提供該類也沒什麼,我們自己也可以寫一個類似的。IntentService之餘Service,類似於HandlerThread之於Handler。
希望本文對大家理解IntentService有所幫助。
相關閱讀:
Android中startService的使用及Service生命週期
Android中HandlerThread的使用及原理解析
相關文章
- Android中IntentService原始碼分析AndroidIntent原始碼
- Android IntentService 的使用和解析AndroidIntent
- HandlerThread和IntentService原始碼解析threadIntent原始碼
- Android 原始碼分析之 EventBus 的原始碼解析Android原始碼
- Android Handler 原始碼解析Android原始碼
- Android Retrofit原始碼解析Android原始碼
- Android原始碼解析-LiveDataAndroid原始碼LiveData
- Android setContentView原始碼解析AndroidView原始碼
- Android——LruCache原始碼解析Android原始碼
- IntentService的使用Intent
- Android 8.1 Handler 原始碼解析Android原始碼
- Android LayoutInflater Factory 原始碼解析Android原始碼
- [Android] Retrofit原始碼:流程解析Android原始碼
- Android View 原始碼解析(一) - setContentViewAndroidView原始碼
- Android AccessibilityService機制原始碼解析Android原始碼
- Android Retrofit原始碼解析:都能看懂的Retrofit使用詳解Android原始碼
- Android 進階之HandlerThread 使用場景及原始碼解析Androidthread原始碼
- TextWatcher的使用及原始碼解析原始碼
- redis原始碼解析----epoll的使用Redis原始碼
- React Native 0.55.4 Android 原始碼分析(Java層原始碼解析)React NativeAndroid原始碼Java
- Android原始碼完全解析——View的Measure過程Android原始碼View
- android面試——開源框架的原始碼解析Android面試框架原始碼
- Android面試相關 - IntentServiceAndroid面試Intent
- Android 網路框架 Retrofit 原始碼解析Android框架原始碼
- Android系統原始碼目錄解析Android原始碼
- weex原始碼解析(四)- android引入sdk原始碼Android
- 從原始碼角度談談AsyncTask的使用及其原理原始碼
- Android原始碼分析(LayoutInflater.from(this).inflate(resId,null);原始碼解析)Android原始碼Null
- GYHttpMock:使用及原始碼解析HTTPMock原始碼
- android apk安裝過程原始碼解析AndroidAPK原始碼
- Android NFC技術解析,附Demo原始碼Android原始碼
- Android OkHttp原始碼解析入門教程(一)AndroidHTTP原始碼
- Android OkHttp原始碼解析入門教程(二)AndroidHTTP原始碼
- Android進階必學retrofit原始碼解析Android原始碼
- Android Studio中的外掛ButterKnife的配置及其使用方法Android
- Android View 原始碼解析(三) – View的繪製過程AndroidView原始碼
- android原始碼學習-Handler機制及其六個核心點Android原始碼
- Android 9.0 原始碼_機制篇 -- 全面解析 HandlerAndroid原始碼
- Android八門神器(一):OkHttp框架原始碼解析AndroidHTTP框架原始碼