在前面文章我們分析了四大元件中的兩個:Broadcast和Activity,這章我們分析四大元件中的服務(Service)的啟動過程。Service的啟動方式有兩種:一種是startService,一種是bindService;第一種通常是開啟一個服務執行後臺任務,不進行通訊,第二章通過是啟動服務進行通訊。下面我們就根據這兩種啟動方式來講Service的啟動流程以及unbindService和stopService流程。
Service啟動流程-startService
首先來看啟動流程時序圖:
Step0.ContextImpl.startService
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, mUser);
}
複製程式碼
呼叫當前類中的startServiceCommon方法。
Step1.ContextImpl.startServiceCommon
private ComponentName startServiceCommon(Intent service, UserHandle user) {
try {
// 檢驗Intent,元件和包名不能為空
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
// 通過getDefault方法獲取AMS的一個代理物件(ActivityManagerProxy),然後呼叫這個代理物件
// 的startService方法來請求AMS啟動Service
ComponentName cn = ActivityManagerNative.getDefault().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), getOpPackageName(), user.getIdentifier());
...
return cn;
} catch (RemoteException e) {
...
}
}
複製程式碼
首先驗證Intent中傳遞的元件名是否為空,為什麼判斷下面我們介紹,接著通過代理物件ActivityManagerProxy,通過Binder呼叫AMS(ActivityManagerService)中的對應方法startService。我們先看包名驗證。
Step2.ContextImpl.validateServiceIntent
private void validateServiceIntent(Intent service) {
if (service.getComponent() == null && service.getPackage() == null) {
if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
IllegalArgumentException ex = new IllegalArgumentException(
"Service Intent must be explicit: " + service);
throw ex;
} else {
Log.w(TAG, "Implicit intents with startService are not safe: " + service
+ " " + Debug.getCallers(2, 3));
}
}
}
複製程式碼
這裡給出瞭如果系統在Android5.0及以上版本,啟動服務必須為顯式啟動,否則丟擲異常,這個情況我們在剛開始在高於5.0系統都會遇到過,限制就在這裡,所以,5.0及以上系統必須用顯式的方式啟動服務。
3.AMP.startService
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, String callingPackage, int userId) throws RemoteException {
...
// 通過Binder物件mRemote向AMS傳送一個型別為START_SERVICE_TRANSACTION的程式間通訊請求,
// 然後會呼叫AMS中的對應的startService方法
mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
...
return res;
}
複製程式碼
看過前面文章的對這一段程式碼應該很熟悉了,這個就是通過Binder呼叫AMS中對應方法的。所以我們直接看AMS。
4.AMS.startService
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, String callingPackage, int userId)
throws TransactionTooLargeException {
...
synchronized (this) {
...
ComponentName res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
複製程式碼
這裡的mServices是ActiveServices,因此呼叫的是ActiveServices中的startServiceLocked方法。
5.ActiveServices.startServiceLocked
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, String callingPackage, final int userId)
throws TransactionTooLargeException {
...
// 解析service這個Intent,就是解析在AndroidManifest.xml定義的Service標籤的intent-filter相關內容
// 並將其內容儲存在Service的record(ServiceRecord)中
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage,
callingPid, callingUid, userId, true, callerFg, false);
...
// 每一個Service元件都使用一個ServiceRecord物件來描述,就像每一個Activity都是用一個ActivityRecord
// 物件來描述一樣
ServiceRecord r = res.record;
...
// 加入啟動服務列表
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
final ServiceMap smap = getServiceMap(r.userId);
boolean addToStarting = false;
// 如果是非前臺(後臺)程式呼叫
if (!callerFg && r.app == null
&& mAm.mUserController.hasStartedUserState(r.userId)) {
// 獲取啟動服務所在程式
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
...
} else if (DEBUG_DELAYED_STARTS) {
...
}
return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}
複製程式碼
這裡做的主要是啟動服務前的準備工作,首先是解析Intent攜帶的引數,並將這些內容儲存在用來描述Service的ServiceRecord物件中儲存起來,並將該物件放到等待啟動服務的列表中。然後呼叫startServiceInnerLocked啟動服務。在上面呼叫retrieveServiceLocked函式解析的過程中先去判斷AMS中是否存在引數為service對應的ServiceRecord物件,如果存在說明已經啟動過該服務,如果不存在,說明是第一次啟動該服務。
8.ActiveServices.startServiceInnerLocked
ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r,
boolean callerFg, boolean addToStarting) throws TransactionTooLargeException {
ServiceState stracker = r.getTracker();// 獲取服務狀態
...
r.callStart = false;// 是否呼叫onStart方法
...
// 啟動ServiceRecord物件r所描述的一個Service元件,即Server元件
String error = bringUpServiceLocked(r, service.getFlags(), callerFg, false, false);
...
return r.name;
}
複製程式碼
這裡比較簡單主要是呼叫bringUpServiceLocked喚起服務。
Step9.ActiveServices.bringUpServiceLocked
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg,
boolean whileRestarting, boolean permissionsReviewRequired)
throws TransactionTooLargeException {
// 這裡的r.app.thread是一個ApplicationThread物件,ApplicationThread是用來AMS和應用程式通訊的工具,
// 如果服務中的這個thread不為空說明已經和該Service存在通訊了,也就是說已經啟動了該服務了。
// 如果服務已經存在,呼叫startService的時候會執行Service.onStartCommand,
// 只有首次啟動服務才會呼叫onCreate方法
if (r.app != null && r.app.thread != null) {
// 執行Service.onStartCommand方法過程
sendServiceArgsLocked(r, execInFg, false);
return null;
}
...
// Make sure that the user who owns this service is started. If not,
// we don`t want to allow it to run.
// 確保正在啟動服務的使用者已經啟動,否則不允許執行
if (!mAm.mUserController.hasStartedUserState(r.userId)) {
...
bringDownServiceLocked(r);
return msg;
}
...
// 是不是獨立程式
final boolean isolated = (r.serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0;
// 首先獲取ServiceRecord物件r所描述的Service元件的android:process屬性,並儲存在procName中
final String procName = r.processName;
ProcessRecord app;
if (!isolated) {// 要啟動的服務不是獨立程式
// 如果不是獨立程式,通過程式名稱和uid查詢是否已經存在一個對應的ProcessRecord物件app,如果存在,
// 說明用來執行這個Service元件的應用程式已經存在了,因此下面的realStartServiceLocked函式在
// ProcessRecord物件app所描述的應用程式程式中啟動這個Service元件。
app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false);
if (app != null && app.thread != null) {// 程式存在,並且該程式已經與AMS通訊過,那麼直接啟動服務
try {
...
// 啟動服務
realStartServiceLocked(r, app, execInFg);
return null;
} catch (TransactionTooLargeException e) {
...
}
// If a dead object exception was thrown -- fall through to
// restart the application.
}
} else {// 如果要啟動的程式是獨立程式
...
}
// 如果要啟動的Service所在程式沒有啟動
if (app == null && !permissionsReviewRequired) {
// 啟動Service所需要的程式
if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
"service", r.name, false, isolated, false)) == null) {
...
// 啟動失敗
bringDownServiceLocked(r);
return msg;
}
...
}
...
if (r.delayedStop) {// 如果是延遲停止的服務
// Oh and hey we`ve already been asked to stop!
r.delayedStop = false;
if (r.startRequested) {
// 停止服務
stopServiceLocked(r);
}
}
return null;
}
複製程式碼
這裡是啟動服務最重要的部分,根據不同的情況進行不同的處理。首先是判斷服務所在程式是否存在,如果存在呼叫sendServiceArgsLocked方法,最終根據條件,如果服務存在呼叫服務的onStartCommand方法;然後判斷被啟動服務的使用者是否已經被啟動,如果沒有則停止服務,也就是呼叫bringDownServiceLocked方法,最終呼叫服務的onDestroy方法;然後判斷非獨立程式的服務,如果程式存在並且服務未啟動的開始正式啟動服務,呼叫realStartServiceLocked方法,最終呼叫onCreate方法;然後判斷如果程式不存在,要啟動程式,並且在app啟動後啟動服務,這裡會呼叫啟動失敗,停止啟動,因為程式啟動後會啟動該服務,這個過程在前面我們講過,這裡不再分析這種情況。最後是如果是延遲停止的服務這裡直接停止該服務。下面我們按順序分析這幾種情況。
Step10.ActiveServices.sendServiceArgsLocked
private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
boolean oomAdjusted) throws TransactionTooLargeException {
// 等待啟動服務個數
final int N = r.pendingStarts.size();
if (N == 0) {
return;
}
while (r.pendingStarts.size() > 0) {
...
try {
..
// 標記啟動服務開始
bumpServiceExecutingLocked(r, execInFg, "start");
...
// 傳送訊息,傳動到ApplicationThread中的scheduleServiceArgs方法,最終會呼叫onStartCommand
r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent);
} catch (TransactionTooLargeException e) {
...
}
}
}
複製程式碼
上面Step5提到啟動的服務都要先放到等待啟動服務列表中,因此這裡先判斷服務列表是否存在要啟動的服務,如果不存在則不再繼續執行,如果存在,迴圈啟動服務,這裡呼叫scheduleServiceArgs方法,其實在前面分析了很多遍,最終會傳送訊息ActivityThread中的Handler中的handleMessage中進行處理,然後呼叫ActivityThread中的handleServiceArgs方法。
Step13.ActivityThread.handleServiceArgs
private void handleServiceArgs(ServiceArgsData data) {
// 獲取Service物件
Service s = mServices.get(data.token);
if (s != null) {
try {
...
if (!data.taskRemoved) {// 任務沒有被移除的話,呼叫Service.onStartCommand方法
res = s.onStartCommand(data.args, data.flags, data.startId);
} else {// 否則呼叫被移除方法
s.onTaskRemoved(data.args);
res = Service.START_TASK_REMOVED_COMPLETE;
}
...
} catch (Exception e) {
...
}
}
}
複製程式碼
根據Service對應的token去快取中獲取服務,如果有該服務那麼呼叫服務的onStartCommand方法,如果不存在那麼就要繼續往下走建立服務。
Step15.ActiveServices.bringDownServiceLocked
這個方法主要是處理停止服務的方法,裡面主要是斷開連線,解除繫結,然後銷燬服務,因為這個過程是在服務停止時會呼叫,所以在後面介紹,這裡先不介紹了。
Step16.ActiveServices.realStartServiceLocked
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
...
boolean created = false;
...
// 傳送資訊到主執行緒,準備呼叫Service.onCreate方法
// 請求ProcessRecord物件app描述的應用程式程式將ServiceRecord獨享r所描述的Service元件啟動起來。
// ServiceRecord物件r所描述的Service元件啟動完成之後,AMS就需要將它連線到一個請求繫結它的一個
// Activity元件中,這是通過呼叫AMS類的另一個成員函式requestServiceBindingLocked來實現的
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
...
// 準備呼叫Service.onBind方法
requestServiceBindingsLocked(r, execInFg);
...
// 準備呼叫Service.onStartCommand方法
sendServiceArgsLocked(r, execInFg, true);
...
}
複製程式碼
啟動服務呼叫三個方法,首先是通過app.thread.scheduleCreateService方法呼叫onCreate方法,然後通過requestServiceBindingsLocked方法呼叫Service.onBind方法,然後通過sendServiceArgsLocked方法呼叫Service.onStartCommand方法,其中最後一個方法我們分析過了,所以我們只分析前兩個。第一很簡單了,最終呼叫ActivityThread中的handleCreateService方法。
Step18.ActivityThread.handleCreateService
private void handleCreateService(CreateServiceData data) {
...
// 獲取一個用來描述即將要啟動的Service元件所在的應用程式的LoadedApk物件,並將它儲存在packageInfo
// 變數中(每一個應用程式都使用一個LoadedApk物件來描述,通過它就能方位到它所描述的應用程式的資源)
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
...
// 獲取類載入器
java.lang.ClassLoader cl = packageInfo.getClassLoader();
// 通過類載入器將CreateServiceData物件data描述的一個Service元件載入的記憶體中,並且建立它的一個
// 例項,儲存在Service物件service中。因為CreateServiceData物件data描述的Service元件即為應用
// 程式的Ashmem中的Server元件,因此,Service物件service指向的Service元件實際上是一個Server元件
service = (Service) cl.loadClass(data.info.name).newInstance();
...
// 初始化一個ContextImpl物件context,用來為前面所建立的Service物件service的執行上下文環境,
// 通過它可以訪問特定的應用程式資源,以及啟動其他應用程式元件
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
// 建立一個Application物件app,用來描述Service物件service所屬的應用程式。
Application app = packageInfo.makeApplication(false, mInstrumentation);
// 使用Application物件app,ContextImpl物件context和CreateServiceData物件data來初始化
// Service物件service
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
// 呼叫Service的onCreate方法
service.onCreate();
// 以token為關鍵字儲存Service物件service到mServices中,服務啟動完成
mServices.put(data.token, service);
...
} catch (Exception e) {
...
}
}
複製程式碼
首先獲取LoadedApk物件,然後通過類載入器載入Service類,初始化Context,獲取對應的Application,如果存在直接返回,如果該應用還沒啟動則直接建立該Application,然後通過Service的attach方法將對應的資訊放置到Service中,這裡面就包含ActivityThread,因此我們在Step9中可以通過這個來判斷Service是不是被啟動了,然後呼叫onCreate方法,建立完成後,將該服務以token為鍵,Service為值放入到快取中,這樣我們前面獲取的時候就只從這裡獲取的,因此如果服務啟動了換粗就會存在,否則不存在。下面我們分析onBind方法。
Step20.ActiveServices.requestServiceBindingsLocked
// 引數r指向一個ServiceRecord,表示一個已經啟動的Service元件
private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
throws TransactionTooLargeException {
for (int i = r.bindings.size() - 1; i >= 0; i--) {
// 每一個IntentBindRecord物件都用來描述若干個需要將ServiceRecord物件r所描述的Service元件
// 繫結到它們裡面的應用程式程式
IntentBindRecord ibr = r.bindings.valueAt(i);
if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
break;
}
}
}
複製程式碼
for迴圈呼叫onBind。
Step21.ActiveServices.requestServiceBindingLocked
// 引數rebind表示是否需要將ServiceRecord物件r所描述的Service元件重新繫結到IntentBindRecord物件i
// 所描述的應用程式程式中如果為false,則說明為第一繫結
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
...
// 檢查AMS是否已經為IntentBindRecord物件i所描述的應用程式程式請求過ServiceRecord物件r所描述的
// Service元件返回其內部的一個Binder本地物件。如果還沒有請求requested為false並且apps的數量大於0
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
...
//會執行到Service的onBind方法
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
if (!rebind) {
// 設定為true,防止重複請求
i.requested = true;
}
...
}
}
return true;
}
複製程式碼
這裡通過呼叫r.app.thread.scheduleBindService方法,最終呼叫到ActivityThread中的handleBindService方法。
Step23.ActivityThread.handleBindService
private void handleBindService(BindServiceData data) {
// 通過token來獲得一個描述Service元件的Service物件
Service s = mServices.get(data.token);
if (s != null) {
try {
...
if (!data.rebind) {// 首次繫結
// 獲取一個實現了IBinder介面的Binder物件
IBinder binder = s.onBind(data.intent);
...
} else {
s.onRebind(data.intent);
...
}
...
} catch (Exception e) {
...
}
}
}
複製程式碼
現獲取服務,然後判斷是再次繫結還是首次繫結,如果是首次繫結呼叫Service.onBind方法,如果是再次繫結呼叫Service.onRebind方法。到這裡服務的啟動就完成了,其他一些操作就不分析了。下面我們先分析另外一個啟動流程bindService,最後分析停止服務流程。
Service啟動流程-bindService
首先來看繫結流程時序圖:
Step1.ContextImpl.bindService
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
Process.myUserHandle());
}
複製程式碼
這裡是繫結服務的入口位置,呼叫bindServiceCommon方法。
Step2.ContextImpl.bindServiceCommon
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
IServiceConnection sd;
...
// mPackageInfo型別是LoadedApk
if (mPackageInfo != null) {
// 將ServiceConnection物件conn封裝成一個實現了IServiceConnection介面的Binder本地物件sd
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
} else {
...
}
...
try {
IBinder token = getActivityToken();
...
// 通過呼叫代理物件ActivityManagerProxy的bindService方法將前面獲得的sd物件,以及Intent物件
// service等資訊傳送給AMS,以便AMS可以將ServiceConnection元件啟動起來
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
...
return res != 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
複製程式碼
首先呼叫LoadedApk中的getServiceDispatcher方法獲取實現IServiceConnection介面的Binder物件ServiceDispatcher.InnerConnection。然後呼叫AMP中的bindService方法,然後通過Binder通訊呼叫AMS中的bindService方法。
Step3.LoadedApk.getServiceDispatcher
// 每一個繫結過Service元件的Activity元件在LoadedApk類中都有一個對應的ServiceDispatcher物件,它負責將
// 這個被繫結的Service元件與繫結它的Activity元件關聯起來,這些ServiceDispatcher儲存在map中
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
synchronized (mServices) {
LoadedApk.ServiceDispatcher sd = null;
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
// 檢查成員變數mServices中是否存在一個以ServiceConnection物件c為關鍵字的ServiceDispatcher
// 物件sd,如果不存在,則建立一個並且以context為關鍵字儲存到mServices中
if (map != null) {
sd = map.get(c);
}
if (sd == null) {
sd = new ServiceDispatcher(c, context, handler, flags);
if (map == null) {
map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler);
}
// 呼叫前面獲取到的ServiceDispatcher物件sd的成員函式getIServiceConnection來獲取一個實現了
// IServiceConnection介面的本地Binder物件
return sd.getIServiceConnection();
}
}
複製程式碼
首先根據ServiceConnection取快取中獲取,如果沒有要初始化一個ServiceDispatcher物件,然後獲取ServiceDispatcher.InnerConnection物件並且返回。
Step6.AMP.bindService
public int bindService(IApplicationThread caller, IBinder token,
Intent service, String resolvedType, IServiceConnection connection,
int flags, String callingPackage, int userId) throws RemoteException {
...
// 通過Binder物件mRemote向AMS傳送一個型別為BIND_SERVICE_TRANSACTION的程式間通訊請求
mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
...
return res;
}
複製程式碼
這裡通過Binder呼叫AMS中對應的方法。
Step7.AMS.bindService
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String callingPackage,
int userId) throws TransactionTooLargeException {
...
synchronized (this) {
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, callingPackage, userId);
}
}
複製程式碼
這裡呼叫ActiveServices.bindServiceLocked方法。
Step8.ActiveServices.bindServiceLocked
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "bindService: " + service
+ " type=" + resolvedType + " conn=" + connection.asBinder()
+ " flags=0x" + Integer.toHexString(flags));
// 根據caller來獲取一個ProcessRecord物件callerApp用來描述AMS執行繫結Service元件操作的一個Activity
// 元件所執行在的應用程式程式
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
...
ActivityRecord activity = null;
if (token != null) {
// 通過token來獲得一個ActivityRecord物件activity,用來描述正在請求AMS執行繫結Service元件
// 操作的一個Activity元件
activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
}
}
...
final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND;
// 是否是繫結了外部服務,這個服務不是應用中的服務,而是外部獨立的服務(我們通常啟動服務都是應用內部的服務)
final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0;
// 根據引數service來得到一個ServiceRecord物件s,用來描述即將被繫結的Service元件
ServiceLookupResult res =
retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(),
Binder.getCallingUid(), userId, true, callerFg, isBindExternal);
...
// 呼叫ServiceRecord物件s的成員函式retrieveAppBindingLocked來得到一個AppBindRecord物件b,
// 表示ServiceRecord物件s所描述的Service元件是繫結在ProcessRecord物件callerApp所描述的一個
// 應用程式程式中的
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
// 將前面獲得的APPBindRecord物件、ActivityRecord物件Activity以及引數connection封裝成一個
// ConnectionRecord物件s所描述的一個Service元件,並且這個Activity元件是執行在
// ProcessRecord物件callerApp所描述的一個應用程式中的
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
// 由於一個Service元件可能會被同一個應用程式程式中的多個Activity元件使用同一個InnerConnection
// 物件來繫結,因此,在AMS中,用來描述該Service元件的ServiceRecord物件就有可能會對應有多個
// ConnectionRecord物件。在這種情況下,這些ConnectionRecord物件就會被儲存在一個列表中。
// 這個列表最終會儲存在對應的ServiceRecord物件的成員變數Connection所描述的HashMap中,並且以
// 它裡面的ConnectionRecord物件共同使用的一個InnerConnection代理物件的IBinder介面為關鍵字
// 引數connection是一個InnerConnection代理物件,因此可以獲取它的一個IBinder介面binder
IBinder binder = connection.asBinder();
// 檢測在ServiceRecord物件s中是否存在一個以IBinder介面binder為關鍵字的列表clist,如果不存在
// 建立一個,並且將clist以binder為關鍵字放到ServiceRecord物件成員變數connections中
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist == null) {// 沒有繫結過
clist = new ArrayList<ConnectionRecord>();
s.connections.put(binder, clist);
}
clist.add(c);// 新增繫結列表
b.connections.add(c);// 新增到關聯應用和服務的物件AppBindRecord中記錄繫結列表中
if (activity != null) {
if (activity.connections == null) {// 這裡判斷Activity裡面是否繫結過服務
activity.connections = new HashSet<ConnectionRecord>();
}
// 新增到描述Activity的ActivityRecord物件中記錄繫結服務的列表中
activity.connections.add(c);
}
...
// 從記錄該服務所有繫結列表中獲取是否存在繫結列表
clist = mServiceConnections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
mServiceConnections.put(binder, clist);
}
clist.add(c);// 新增都所有列表中
// 從前面可知flags的Context.BIND_AUTO_CREATE位等於1,因此會呼叫bringUpServiceLocked來啟動
// ServiceRecord物件s所描述的一個Service元件,等到這個Service元件啟動以後,AMS再將它與
// ActivityRecord物件Activity所描述的一個Activity繫結自來
if ((flags & Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired) != null) {
return 0;
}
}
...
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
try {
// 這裡的c.conn是ServiceDispatcher.InnerConnection物件,這裡最終呼叫
// ServiceDispatcher中的doConnected方法
c.conn.connected(s.name, b.intent.binder);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + s.shortName
+ " to connection " + c.conn.asBinder()
+ " (in " + c.binding.client.processName + ")", e);
}
// If this is the first app connected back to this binding,
// and the service had previously asked to be told when
// rebound, then do so.
if (b.intent.apps.size() == 1 && b.intent.doRebind) {// 重新繫結
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {// 首次繫結
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
getServiceMap(s.userId).ensureNotStartingBackground(s);
} finally {
Binder.restoreCallingIdentity(origId);
}
return 1;
}
複製程式碼
這裡程式碼比較多,所以寫的註釋也比較多,前面主要是判斷是否繫結過該服務,並對InnerConnection進行快取,放置到各個列表中,讓Activity,Service進行聯絡。最後呼叫requestServiceBindingLocked方法,這個方法呼叫兩次,一次是首次繫結,一個是重新繫結。
Step9.AMS.requestServiceBindingLocked
// 引數rebind表示是否需要將ServiceRecord物件r所描述的Service元件重新繫結到IntentBindRecord物件i
// 所描述的應用程式程式中如果為false,則說明為第一繫結
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
...
// 檢查AMS是否已經為IntentBindRecord物件i所描述的應用程式程式請求過ServiceRecord物件r所描述的
// Service元件返回其內部的一個Binder本地物件。如果還沒有請求requested為false並且apps的數量大於0
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r, execInFg, "bind");
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
// 會執行到Service的onBind方法
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
if (!rebind) {
// 設定為true,防止重複請求
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
...
}
return true;
}
複製程式碼
這裡是判斷首次繫結還是重新繫結,這兩種繫結都會執行繫結步驟,這裡有個引數i.requested,代表是否是首次繫結,如果預設值是false,如果不是重新繫結,那麼執行完繫結就會設定為true。然後呼叫thread.scheduleBindServic方法,這個方法講了很多次了,最終呼叫ApplicationThread.scheduleBindService方法,然後通過handler呼叫ActivityThread.handleBindService方法。
Step10.ActivityThread.handleBindService
private void handleBindService(BindServiceData data) {
// 通過token來獲得一個描述Service元件的Service物件
Service s = mServices.get(data.token);
...
if (!data.rebind) {// 首次繫結
// 獲取一個實現了IBinder介面的Binder物件
IBinder binder = s.onBind(data.intent);
// 呼叫AMS代理物件的成員函式publishService,將前面得到的Binder本地物件傳遞給AMS
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
}
複製程式碼
這裡如果是首次繫結呼叫onBind方法,否則呼叫onRebind方法,在首次呼叫繫結後還有publishService方法,自己看一下,不在詳細分析了。這樣繫結流程就分析完了,難度不大。下面我們開分析解綁過程。
Service解綁流程-unbindService
首先來看解綁流程時序圖:
Step1.ContextImpl.unbindService
public void unbindService(ServiceConnection conn) {
...
if (mPackageInfo != null) {
// 獲取將ServiceConnection物件conn封裝成一個實現了IServiceConnection介面的Binder
// 本地物件sd(ServiceDispatcher.InnerConnection)
IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
getOuterContext(), conn);
try {
ActivityManagerNative.getDefault().unbindService(sd);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
throw new RuntimeException("Not supported in system context");
}
}
複製程式碼
IServiceConnection物件的獲取我們前面分析過了,這裡直接過,然後呼叫AMP.unbindService方法,最終呼叫AMS.unbindService方法。
Step3.AMS.unbindService
public boolean unbindService(IServiceConnection connection) {
synchronized (this) {
return mServices.unbindServiceLocked(connection);
}
}
複製程式碼
這裡呼叫ActiveServices.unbindServiceLocked方法。
Step4.ActiveServices.unbindServiceLocked
boolean unbindServiceLocked(IServiceConnection connection) {
IBinder binder = connection.asBinder();
// 獲取快取中繫結列表
ArrayList<ConnectionRecord> clist = mServiceConnections.get(binder);
...
try {
while (clist.size() > 0) {
ConnectionRecord r = clist.get(0);
removeConnectionLocked(r, null, null);
if (clist.size() > 0 && clist.get(0) == r) {
// In case it didn`t get removed above, do it now.
Slog.wtf(TAG, "Connection " + r + " not removed for binder " + binder);
clist.remove(0);
}
....
}
} finally {
Binder.restoreCallingIdentity(origId);
}
return true;
}
複製程式碼
首先從AMS中獲取繫結過的列表,如果存在說明繫結過,然後呼叫removeConnectionLocked移除連線。
Step5.ActiveServices.removeConnectionLocked
void removeConnectionLocked(
ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
// 獲取連線中的Binder物件
IBinder binder = c.conn.asBinder();
// 獲取連線應用、服務和連線列表的客戶端
AppBindRecord b = c.binding;
// 獲取描述服務的物件
ServiceRecord s = b.service;
// 根據Binder物件獲取服務繫結的連線列表
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist != null) {
clist.remove(c);// 移除該連線物件
if (clist.size() == 0) {// 如果列表為空,移除該列表
s.connections.remove(binder);
}
}
// 從連線應用、服務和連線列表的客戶端中的連線列表中移除
b.connections.remove(c);
...
// 從總的快取列表中移除
clist = mServiceConnections.get(binder);
if (clist != null) {
clist.remove(c);
if (clist.size() == 0) {
mServiceConnections.remove(binder);
}
}
mAm.stopAssociationLocked(b.client.uid, b.client.processName, s.appInfo.uid, s.name);
// 如果連線應用、服務和連線列表的客戶端中的連線列表為空了,說明沒有繫結了,那麼移除該客戶端
if (b.connections.size() == 0) {
b.intent.apps.remove(b.client);
}
if (!c.serviceDead) {// 服務還存在
if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0
&& b.intent.hasBound) {
try {
...
b.intent.doRebind = false;
s.app.thread.scheduleUnbindService(s, b.intent.intent.getIntent());
} catch (Exception e) {
Slog.w(TAG, "Exception when unbinding service " + s.shortName, e);
serviceProcessGoneLocked(s);
}
}
...
}
}
複製程式碼
在繫結服務的時候進行各種快取,加入各種列表,那麼在接觸繫結的時候就有從之前加入的列表中刪除,然後執行接觸繫結。
Step7.ActivityThread.handleUnbindService
private void handleUnbindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
...
boolean doRebind = s.onUnbind(data.intent);// 呼叫解綁回撥
...
} catch (Exception e) {
...
}
}
}
複製程式碼
呼叫Service的onUnbind接觸繫結。到這裡接觸繫結就分析完了,過程比較簡單,只是上面的各種列表搞清楚要多看看。最後就剩下了stopService,我們一口氣就將它分析完。
Service停止流程-stopService
首先來看停止服務流程時序圖:
Step1.ContextImpl.stopSevice
// 停止服務
@Override
public boolean stopService(Intent service) {
warnIfCallingFromSystemProcess();
return stopServiceCommon(service, mUser);
}
複製程式碼
呼叫stopServiceCommon停止服務。
Step2.ContextImpl.stopServiceCommon
private boolean stopServiceCommon(Intent service, UserHandle user) {
try {
// 檢驗Intent,元件和包名不能為空
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
int res = ActivityManagerNative.getDefault().stopService(
mMainThread.getApplicationThread(), service,
service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
...
return res != 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
複製程式碼
這裡最終呼叫AMS的stopService方法。
Step3.AMS.stopService
public int stopService(IApplicationThread caller, Intent service,
String resolvedType, int userId) {
...
synchronized (this) {
return mServices.stopServiceLocked(caller, service, resolvedType, userId);
}
}
複製程式碼
這裡就是簡單的呼叫ActiveServices.stopServiceLocked方法。
Step4.ActiveServices.stopServiceLocked
int stopServiceLocked(IApplicationThread caller, Intent service,
String resolvedType, int userId) {
// 根據caller獲取呼叫者程式
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
...
if (r != null) {
...
stopServiceLocked(r.record);
...
return -1;
}
return 0;
}
複製程式碼
簡單的呼叫了stopServiceLocked方法。
Step6.ActiveServices.stopServiceLocked
private void stopServiceLocked(ServiceRecord service) {
...
bringDownServiceIfNeededLocked(service, false, false);
}
複製程式碼
我們前面分析過bringUp的是啟動服務,因此對應的bringDown的是結束服務。
Step7.ActiveServices.bringDownServiceIfNeededLocked
// 停止服務
private final void bringDownServiceIfNeededLocked(ServiceRecord r, boolean knowConn,
boolean hasConn) {
...
bringDownServiceLocked(r);
}
複製程式碼
這裡只是簡單的呼叫了bringDownServiceLocked方法,這個方法我們在服務啟動時也遇到過,只是沒有分析,我們放到了停止服務的流程中來分析,下面我們看看詳細程式碼。
Step8.ActiveServices.bringDownServiceLocked
private final void bringDownServiceLocked(ServiceRecord r) {
for (int conni = r.connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> c = r.connections.valueAt(conni);
for (int i = 0; i < c.size(); i++) {
...
// 斷開服務連線
cr.conn.connected(r.name, null);
...
}
}
// Tell the service that it has been unbound.
if (r.app != null && r.app.thread != null) {
for (int i = r.bindings.size() - 1; i >= 0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
if (ibr.hasBound) {// 所有服務與客戶端已經解綁
try {
...
// 呼叫Service的onUnbind方法
r.app.thread.scheduleUnbindService(r,
ibr.intent.getIntent());
} catch (Exception e) {
...
}
}
}
}
...
if (r.app != null) {// 服務程式存在
...
if (r.app.thread != null) {
updateServiceForegroundLocked(r.app, false);
try {
...
// 停止服務,呼叫onDestroy方法
r.app.thread.scheduleStopService(r);
} catch (Exception e) {
...
...
}
複製程式碼
這裡主要有三步,第一步,斷開服務連線,這個方法我們前面提到過,可以根據前面提到的去看看如何斷開連線的;第二步,如果已經繫結了服務要解除繫結,這個在上面解除繫結的時候分析了該過程,因此這裡就不再重複了;第三步,如果服務存在,則停止服務。我們開始分析第三步,第三步這裡最終呼叫ActivityThread的handleStopService方法。
Step13.ActivityThread.handleStopService
private void handleStopService(IBinder token) {
Service s = mServices.remove(token);
...
s.onDestroy();
...
}
複製程式碼
這裡如果服務存在則呼叫服務的onDestroy方法,到這裡服務的停止也就結束了。從上面看服務的整個流程相對於Activity簡單的多。很容易就看懂了。其實還有一個IntentService,整個服務繼承Service,只不過裡面多了一個Handler,我們看看這段程式碼:
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);
}
}
複製程式碼
如果有Handler就有傳送訊息的地方,那麼傳送訊息在哪裡呢,我們知道當你呼叫服務的時候會走onStartCommand方法:
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
複製程式碼
這裡會呼叫onStart方法:
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
複製程式碼
這裡就是傳送訊息的時候,訊息發出後會由上面的ServiceHandler來處理,我們看到建構函式裡傳入了一個Looper,這個是在onCreate方法中初始化的:
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);
}
複製程式碼
這裡建立了一個Handler執行緒,並且獲取了它的Looper,用來迴圈處理訊息,因此我們知道同時只能處理一個訊息,等前一個訊息處理完了才會處理第二個,以此類推,因此需要同時處理的不能用這個Service,另外在ServiceHandler中有個stopSelf用來在訊息處理完成後停止自己,因此該服務可以說是用完自動停止,不會一直存在,佔用資源。
程式碼地址:
直接拉取匯入開發工具(Intellij idea或者Android studio)
注:
首發地址:www.codemx.cn
Android開發群:192508518
微信公眾賬號:Code-MX
注:本文原創,轉載請註明出處,多謝。