以前作一道面試題:startService 和 bindService 有什麼不一樣?爲何 bindService 能和 Activity 的生命週期聯動?android
前一個問題能夠很快回答出來:生命週期不一樣,結束方式不一樣,交互方式不一樣。面試
後一個問題也能很快想到應該是 Activity 在銷燬的時候順帶把 Service 銷燬了。那麼爲何 startService 不行呢?具體是怎麼實現的呢?若是不對源碼研究一番,彷佛沒法給出讓人信服的回答,因而就有了這篇文章。bash
不管是啓動 Activity,仍是 Service,基本的流程都是 Context -> ActivtityManagerService -> 某些中間類(Activity 是 ActivityStarter、ActivityStack 等,Service 是 ActiveServices) -> ActivityThread。具體的代碼流程比較長,並且不少和本文要探討的主題無關,所以這裏不會詳細分析啓動或綁定的流程,而只會保留與本文相關的部分源碼。app
從 ContextImpl 的 startService 方法開始提及:ide
class ContextImpl extends Context {
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
private ComponentName startServiceCommon(Intent service, boolean requireForeground,
UserHandle user) {
try {
// 檢查 Intent
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
// 啓動 Service
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
// 檢查結果
if (cn != null) {
...
}
return cn;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
複製代碼
ActivityManager.getService() 返回的就是 AMS 自己,而 AMS 只起到一箇中轉的做用,除了一些參數判斷以外,AMS 直接調用了 ActiveServices 的 startServiceLocked:post
public final class ActiveServices {
final ActivityManagerService mAm;
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
throws TransactionTooLargeException {
// 和 Activity 一樣有一個 Record 記錄對應的組件
ServiceRecord r = res.record;
... // 主要是檢查,發現錯誤則拋出異常,或返回 null 等結果
// 設置 ServiceRecord 的數據域
r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
r.delayedStop = false;
r.fgRequired = fgRequired;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants, callingUid));
... // 主要是檢查,發現錯誤則拋出異常,或返回 null 等結果
// 啓動
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
return cmp;
}
}
複製代碼
好了,startService 的流程暫時分析到這裏,後面也沒什麼特別的,最後仍是會由 ActivityThread 來建立 Service 對象,回調相關的生命週期方法等。ui
下面看 bindService 的實現:this
class ContextImpl extends Context {
final @NonNull LoadedApk mPackageInfo;
private final @Nullable IBinder mActivityToken;
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
Process.myUserHandle());
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
...
IServiceConnection sd;
if (mPackageInfo != null) {
// 注意這裏
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
} else {
throw new RuntimeException("Not supported in system context");
}
try {
// 這個 token 是 Activity 啓動時建立的,對應於 Activity 的 mToken 成員
IBinder token = getActivityToken();
...
// 執行綁定流程
int res = ActivityManager.getService().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
...
} catch (RemoteException e) {
...
}
}
@Override
public IBinder getActivityToken() {
return mActivityToken;
}
}
複製代碼
能夠看到,相比 startService,bindService 還在 ContextImpl 執行的時候就已經顯示出了它的不一樣,除了會獲取 Activity 的 token 以外,還有一個很關鍵的調用是 LoadedApk 的 getServiceDispatcher 方法:spa
public final class LoadedApk {
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
= new ArrayMap<>();
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);
if (map != null) {
sd = map.get(c);
}
if (sd == null) {
sd = new ServiceDispatcher(c, context, handler, flags);
if (map == null) {
map = new ArrayMap<>();
mServices.put(context, map); // 記錄
}
map.put(c, sd); // 記錄 ServiceConnection
} else {
sd.validate(context, handler);
}
return sd.getIServiceConnection();
}
}
}
複製代碼
ServiceDispatcher 能夠忽略,主要關注 mServices 這個成員,它記錄了即將綁定到 Activity 的 ServiceConnection。rest
接着看後面的綁定流程,AMS 一樣跳過,直接看 ActiveServices 的實現:
public final class ActiveServices {
final ActivityManagerService mAm;
final ArrayMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections = new ArrayMap<>();
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException {
// 獲取應用進程的信息
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
if (callerApp == null) {
throw new SecurityException(
"Unable to find app for caller " + caller
+ " (pid=" + Binder.getCallingPid()
+ ") when binding service " + service);
}
// 獲取綁定的 Activity 信息
ActivityRecord activity = null;
if (token != null) {
activity = ActivityRecord.isInStackLocked(token);
if (activity == null) {
Slog.w(TAG, "Binding with unknown activity: " + token);
return 0;
}
}
// 獲取 ServiceRecord
ServiceRecord s = res.record;
boolean permissionsReviewRequired = false;
// 啓動 Activity,成功啓動後再啓動 Service
if (mAm.mPermissionReviewRequired) {
if (mAm.getPackageManagerInternalLocked().isPermissionsReviewRequired(
s.packageName, s.userId)) {
RemoteCallback callback = new RemoteCallback(
new RemoteCallback.OnResultListener() {
@Override
public void onResult(Bundle result) {
synchronized(mAm) {
final long identity = Binder.clearCallingIdentity();
try {
...
if (...) {
try {
// 啓動 Service
bringUpServiceLocked(...);
} catch (RemoteException e) {
/* ignore - local call */
}
} else {
...
}
} finally {
...
}
}
}
});
final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);
// 注意 callback
intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);
// 啓動 Activity,成功啓動後回調 callback
mAm.mHandler.post(new Runnable() {
@Override
public void run() {
mAm.mContext.startActivityAsUser(intent, new UserHandle(userId));
}
});
}
}
final long origId = Binder.clearCallingIdentity();
try {
...
// 注意參數 activity(ActivityRecord)
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
// connection 類型爲 IServiceConnection
IBinder binder = connection.asBinder();
// 讓 ActivityRecord 記錄 connections 信息
if (activity != null) {
if (activity.connections == null) {
activity.connections = new HashSet<ConnectionRecord>();
}
activity.connections.add(c);
}
// 讓 ServiceRecord 記錄 connections 信息
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
s.connections.put(binder, clist);
}
clist.add(c);
// 讓自身的成員變量 mServiceConnections 記錄 connections 信息
clist = mServiceConnections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
mServiceConnections.put(binder, clist);
}
clist.add(c);
...
if ((flags&Context.BIND_AUTO_CREATE) != 0) { // 若是設置了綁定後自動啓動
s.lastActivity = SystemClock.uptimeMillis();
// 啓動 Service
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired) != null) {
return 0;
}
}
if (s.app != null && b.intent.received) { // Service 已經在運行中,直接回調 onServiceConnected 便可
// Service is already running, so we can immediately
// publish the connection.
try {
// 回調 onServiceConnected
c.conn.connected(s.name, b.intent.binder, false);
} catch (Exception e) {
...
}
...
} else if (!b.intent.requested) {
// 回調 onBind,內部調用了 scheduleBindService
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
return 1;
}
}
複製代碼
能夠看到,相比 startService,bindService 在啓動 Service 以前作了一些額外的工做:
上面就是 startService 和 bindService 在源碼實現上的主要區別了,下面開始分析 Activity 的 finish 方法,看看 Service 是怎麼隨着 Activity 的銷燬而銷燬的。
和啓動流程相似,finish 的執行流程爲 Activity -> ActivityManager -> ActivitiyStack -> ActivityThread,由於代碼量有些大,並且前三步和本文關係不大,所以這裏直接看 ActivityThread 的實現便可:
public final class ActivityThread {
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
private void handleDestroyActivity(IBinder token, boolean finishing,
int configChanges, boolean getNonConfigInstance) {
// 回調生命週期方法
ActivityClientRecord r = performDestroyActivity(token, finishing,
configChanges, getNonConfigInstance);
if (r != null) {
// 清理 window 資源
cleanUpPendingRemoveWindows(r, finishing);
// 刪除 DecorView
WindowManager wm = r.activity.getWindowManager();
View v = r.activity.mDecor;
if (v != null) {
IBinder wtoken = v.getWindowToken();
if (r.activity.mWindowAdded) {
if (r.mPreserveWindow) {
r.window.clearContentView();
} else {
wm.removeViewImmediate(v);
}
}
// 清除記錄,這個記錄能夠參考 ViewRootImpl 的 setView 方法
if (wtoken != null && r.mPendingRemoveWindow == null) {
WindowManagerGlobal.getInstance().closeAll(wtoken,
r.activity.getClass().getName(), "Activity");
} else if (r.mPendingRemoveWindow != null) {
WindowManagerGlobal.getInstance().closeAllExceptView(token, v,
r.activity.getClass().getName(), "Activity");
}
r.activity.mDecor = null;
}
if (r.mPendingRemoveWindow == null) {
WindowManagerGlobal.getInstance().closeAll(token,
r.activity.getClass().getName(), "Activity");
}
// 使用 Base Context 執行最後的清理步驟
Context c = r.activity.getBaseContext();
if (c instanceof android.app.ContextImpl) {
((ContextImpl) c).scheduleFinalCleanup(
r.activity.getClass().getName(), "Activity");
}
}
// 通知 AMS
if (finishing) {
try {
ActivityManager.getService().activityDestroyed(token);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
mSomeActivitiesChanged = true;
}
}
複製代碼
能夠看到,在 ActivityThread 中,Activity 的銷燬流程共有 4 步:
Service 的解綁邏輯就隱藏在 ContextImpl 裏面,下面看它的實現:
class ContextImpl extends Context {
final @NonNull ActivityThread mMainThread;
final @NonNull LoadedApk mPackageInfo;
// 回調 ActivityThread
final void scheduleFinalCleanup(String who, String what) {
mMainThread.scheduleContextCleanup(this, who, what);
}
// ActivityThread 最終又回調了該方法
final void performFinalCleanup(String who, String what) {
//Log.i(TAG, "Cleanup up context: " + this);
mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
}
}
複製代碼
能夠看到,ContextImpl 只是起到了一箇中轉的做用,最終是交給 LoadedApk 執行的。從方法 removeContextRegistrations 的名字能夠推測出,它的做用是清理註冊到 Context 上的資源:
public final class LoadedApk {
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
= new ArrayMap<>();
private final ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
= new ArrayMap<>();
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
= new ArrayMap<>();
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
= new ArrayMap<>();
public void removeContextRegistrations(Context context,
String who, String what) {
final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
synchronized (mReceivers) {
// 獲取註冊到 Context 上的 BroadcastReceiver
ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
mReceivers.remove(context);
if (rmap != null) {
// 遍歷,逐個註銷
for (int i = 0; i < rmap.size(); i++) {
LoadedApk.ReceiverDispatcher rd = rmap.valueAt(i);
// 打印異常信息
IntentReceiverLeaked leak = new IntentReceiverLeaked(
what + " " + who + " has leaked IntentReceiver "
+ rd.getIntentReceiver() + " that was " +
"originally registered here. Are you missing a " +
"call to unregisterReceiver()?");
leak.setStackTrace(rd.getLocation().getStackTrace());
Slog.e(android.app.ActivityThread.TAG, leak.getMessage(), leak);
if (reportRegistrationLeaks) {
StrictMode.onIntentReceiverLeaked(leak);
}
// 通知 AMS 註銷 BroadcastReceiver
try {
ActivityManager.getService().unregisterReceiver(
rd.getIIntentReceiver());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
mUnregisteredReceivers.remove(context);
}
synchronized (mServices) {
// 獲取綁定到 Context 上的 ServiceConnection
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
mServices.remove(context);
if (smap != null) {
// 遍歷,逐個解綁
for (int i = 0; i < smap.size(); i++) {
LoadedApk.ServiceDispatcher sd = smap.valueAt(i);
// 打印異常信息
ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
what + " " + who + " has leaked ServiceConnection "
+ sd.getServiceConnection() + " that was originally bound here");
leak.setStackTrace(sd.getLocation().getStackTrace());
Slog.e(android.app.ActivityThread.TAG, leak.getMessage(), leak);
if (reportRegistrationLeaks) {
StrictMode.onServiceConnectionLeaked(leak);
}
// 通知 AMS 解綁 ServiceConnection
try {
ActivityManager.getService().unbindService(
sd.getIServiceConnection());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
sd.doForget();
}
}
mUnboundServices.remove(context);
}
}
}
複製代碼
果真,removeContextRegistrations 的做用就是把註冊/綁定到 Context 上的 BroadcastReceiver、ServiceConnection 給註銷/解綁,並拋出異常信息,告訴用戶應該主動地註銷/解綁。unbindService、unregisterReceiver 的流程忽略,無非仍是從相關的列表中刪除一些記錄(好比 activity.connections),並通知 ActivityThread 執行最後的註銷邏輯。
分析完上面的代碼後,如今能夠自信地給出這道面試題的答案了: