Android筆記-service啟動過程分析:bindService原始碼分析、startService和bindService區別
前言:
Service的啟動流程將會分為一個系列來講述。
本系列開始將分析Service的啟動過程。
看這個系列文章之前你所需要知道的知識點:
1. 熟悉service的基本用法。
2. 瞭解bind機制,知道android的客戶端和AMS間通訊流程。
3. 最好學習過activity的啟動流程。
本系列將涉及到以下一些分支:
startService原始碼分析
bindService原始碼分析、startService和bindService區別
第二次startService為什麼沒有呼叫onCreate
為什麼bindService和startService同時呼叫後需要同時呼叫unBind和stop才能使服務停止。
前臺Service原理
今天這一篇將講述bindService原始碼分析:
service啟動過程分析
bindService原始碼分析
bindService、startService區別
分析bindService,不打算像startService一樣,分析整個AMS流程。
因為兩者的AMS流程很相似,所以打算結合startService流程,分析兩者的不同之處。
首先我們要思考一下startService和bindService用法上有什麼不同:
1. 服務端:
區別 | startService | bindService |
---|---|---|
生命週期 | 會呼叫onStartCommend | 會呼叫onBind |
返回值 | onStartCommend沒有返回值 | onBind需要返回一個Bind型別的值 |
2. 客戶端
區別 | startService | bindService |
---|---|---|
啟動 | 用startService來啟動startService | 用bindService來啟動bindService |
引數 | startService引數只需要一個Intent | bindService引數多需要ServiceConnection |
回撥 | 沒有回撥 | 需要重寫ServiceConnection的onServiceConnected方法 |
3、其他區別
區別 | startService | bindService |
---|---|---|
直接UI更新(非廣播或eventbus等其他機制) | 不可以 | 可以 |
跨程序 | 不可以 | 可以 |
區別大概就是以上幾點。可以看出bindService和startService相比,多了一個bind機制,可以讓Service和Activity之間相互通訊。當然,主要目的是為了可以進行跨程序通訊。
ServiceDispatch、InnerConnection
我們先從客戶端的bindService方法開始看,
當然,客戶端經過一系列呼叫最終會執行到ContextImpl的bindServiceCommon方法:
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
IServiceConnection sd;//sd具體實現類是ServiceDispatch
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (mPackageInfo != null) {
//新建一個ServiceDispatch.InnerConnection,第一個引數是:ServiceConnection
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
} else {
throw new RuntimeException("Not supported in system context");
}
//檢查正確性
validateServiceIntent(service);
try {
IBinder token = getActivityToken();
if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
&& mPackageInfo.getApplicationInfo().targetSdkVersion
< android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
flags |= BIND_WAIVE_PRIORITY;
}
//Prepare this {@link Intent} to leave an app process. 跨程序通訊準備
service.prepareToLeaveProcess(this);
//開始AMS通訊,注意引數多了一個sd,該引數包含了ServiceConnection資訊
int res = ActivityManager.getService().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
if (res < 0) {
throw new SecurityException(
"Not allowed to bind to service " + service);
}
return res != 0;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
bindServiceCommon相比startServiceCommend多了一步sd引數的生成。sd引數是什麼?怎麼生成的呢?
先來看下獲取sd的方法mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
:
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) {
if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);
sd = map.get(c);
}
if (sd == null) {
//用ServiceConnection生成一個LoadedApk.ServiceDispatcher
sd = new ServiceDispatcher(c, context, handler, flags);
if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);
if (map == null) {
map = new ArrayMap<>();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler);
}
return sd.getIServiceConnection();//獲取InnerConnection
}
}
ServiceDispatcher的getIServiceConnection方法
private final ServiceDispatcher.InnerConnection mIServiceConnection;
..
IServiceConnection getIServiceConnection() {
return mIServiceConnection;
}
從上面兩段程式碼我們看到sd原來是一個IServiceConnection。看上去是一個跨程序通訊的玩意兒。
這裡涉及到三個類(LoadedApk,ServiceDispatch,InnerConnection)、一個介面IServiceConnection:
LoadedApk,ServiceDispatch,InnerConnection關係如下:
LoadedApk: Local state maintained about a currently loaded .apk.:保持當前載入的.apk的本地狀態。
ServiceDispatch:它是LoadedApk內部類,其建構函式:
ServiceDispatcher(ServiceConnection conn,
Context context, Handler activityThread, int flags) {
mIServiceConnection = new InnerConnection(this);//用自己新建一個InnerConnection
mConnection = conn;//儲存了ServiceConnection
mContext = context;
mActivityThread = activityThread;
mLocation = new ServiceConnectionLeaked(null);
mLocation.fillInStackTrace();
mFlags = flags;
}
InnerConnection:它是ServiceDispatch內部類,繼承了IServiceConnection.Stub,可以跨程序通訊,其原始碼:
private static class InnerConnection extends IServiceConnection.Stub {//繼承IServiceConnection.Stub,可以跨程序
final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
InnerConnection(LoadedApk.ServiceDispatcher sd) {
mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
}
public void connected(ComponentName name, IBinder service, boolean dead)
throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service, dead);
}
}
}
InnerConnection持有了一個ServiceDispatcher,且該connected方法執行的是ServiceDispatcher的connected方法。再看ServiceDispatcher的connected方法:
public void connected(ComponentName name, IBinder service, boolean dead) {
if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 0, dead));
} else {
doConnected(name, service, dead);
}
}
可以看到connected方法會會執行RunConnection的run()方法或者doConnected方法,再看RunConnection類:
private final class RunConnection implements Runnable {
RunConnection(ComponentName name, IBinder service, int command, boolean dead) {
mName = name;
mService = service;
mCommand = command;
mDead = dead;
}
public void run() {
if (mCommand == 0) {
doConnected(mName, mService, mDead);
} else if (mCommand == 1) {
doDeath(mName, mService);
}
}
final ComponentName mName;
final IBinder mService;
final int mCommand;
final boolean mDead;
}
可以看到最終會執行doConnected方法,doConnected方法原始碼:
public void doConnected(ComponentName name, IBinder service, boolean dead) {
ServiceDispatcher.ConnectionInfo old;
ServiceDispatcher.ConnectionInfo info;
...省略一些程式碼
// If there was an old service, it is now disconnected.
if (old != null) {
mConnection.onServiceDisconnected(name);
}
if (dead) {
mConnection.onBindingDied(name);
}
// If there is a new service, it is now connected.
if (service != null) {
//執行ServiceConnection的onServiceConnected
mConnection.onServiceConnected(name, service);
}
}
最終會執行ServiceConnection的onServiceConnected方法。
至於為什麼這麼麻煩的使用這麼多類來生成一個sd,主要是因為涉及到跨程序通訊,而ServiceConnection並不能進行跨程序通訊,只能用ServiceDispatcher.InnerConnection來儲存其資訊,進行跨程序通訊。
以上分析了ServiceConnection是如何被封裝成ServiceDispatcher.InnerConnection的。
我們只要知道2點:
1. 由於ServiceConnection不能跨程序,所以使用了ServiceDispatcher.InnerConnection來儲存ServiceConnection的資訊,
2. ServiceDispatcher.InnerConnection執行connected方法會執行ServiceConnection的onServiceConnected方法。
記住以上兩點,在後面的onServiceConnected我們流程中會用到,繼續回頭看,下面會進行跨程序通訊。
AMS中的bindService
呼叫AMS的bindService,經過一系列呼叫最終同樣會走到realStartServiceLocked。具體呼叫了哪些方法可以看時序圖。總結如下:
bindService(ActivityManagerService)—bindServiceLocked(ActiveServices)—-bringUpServiceLocked(ActiveServices)—-realStartServiceLocked(ActiveServices)。
最終執行的realStartServiceLocked方法,其實在startService中最終也是realStartServiceLocked方法。很神奇,怎麼都走了這個方法?為什麼後面客戶端的流程會不一樣呢(startService執行了onStartCommend,bindService執行了onBind)?那麼我們再來回顧一下realStartServiceLocked方法吧:
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
if (app.thread == null) {
throw new RemoteException();
}
if (DEBUG_MU)
Slog.v(TAG_MU, "realStartServiceLocked, ServiceRecord.uid = " + r.appInfo.uid
+ ", ProcessRecord.uid = " + app.uid);
r.app = app;
r.restartTime = r.lastActivity = SystemClock.uptimeMillis();
final boolean newService = app.services.add(r);
bumpServiceExecutingLocked(r, execInFg, "create");
mAm.updateLruProcessLocked(app, false, null);
mAm.updateOomAdjLocked();
boolean created = false;
try {
if (LOG_SERVICE_START_STOP) {
String nameTerm;
int lastPeriod = r.shortName.lastIndexOf('.');
nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
EventLogTags.writeAmCreateService(
r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
}
synchronized (r.stats.getBatteryStats()) {
r.stats.startLaunchedLocked();
}
mAm.notifyPackageUse(r.serviceInfo.packageName,
PackageManager.NOTIFY_PACKAGE_USE_SERVICE);
app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
//會呼叫onCreate方法
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
r.postNotification();
created = true;
} catch (DeadObjectException e) {
Slog.w(TAG, "Application dead when creating service " + r);
mAm.appDiedLocked(app);
throw e;
} finally {
if (!created) {
// Keep the executeNesting count accurate.
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
// Cleanup.
if (newService) {
app.services.remove(r);
r.app = null;
}
// Retry.
if (!inDestroying) {
scheduleServiceRestartLocked(r, false);
}
}
}
if (r.whitelistManager) {
app.whitelistManager = true;
}
//後續onBind方法會呼叫
requestServiceBindingsLocked(r, execInFg);
updateServiceClientActivitiesLocked(app, null, true);
// If the service is in the started state, and there are no
// pending arguments, then fake up one so its onStartCommand() will
// be called.
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
null, null));
}
//後續onStartCommend方法會呼叫
sendServiceArgsLocked(r, execInFg, true);
if (r.delayed) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "REM FR DELAY LIST (new proc): " + r);
getServiceMap(r.userId).mDelayedStartList.remove(r);
r.delayed = false;
}
if (r.delayedStop) {
// Oh and hey we've already been asked to stop!
r.delayedStop = false;
if (r.startRequested) {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
"Applying delayed stop (from start): " + r);
stopServiceLocked(r);
}
}
}
注意requestServiceBindingsLocked方法,它後續會呼叫bindService。
private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
throws TransactionTooLargeException {
for (int i=r.bindings.size()-1; i>=0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
break;
}
}
}
該方法用到了r.bindings。它是一個ArrayMap,儲存了客戶端的bind訊息:
final ArrayMap<Intent.FilterComparison, IntentBindRecord> bindings
= new ArrayMap<Intent.FilterComparison, IntentBindRecord>();
哈哈,前文中的賣得關子現在看到了,原來是用r.bindings來判斷是否執行onBind方法的啊~。
具體儲存方法在AMS一開始的方法bindServiceLocked中:
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException {
...
//該方法給bindings賦值
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
....
return 1;
}
public AppBindRecord retrieveAppBindingLocked(Intent intent,
ProcessRecord app) {
Intent.FilterComparison filter = new Intent.FilterComparison(intent);
IntentBindRecord i = bindings.get(filter);
if (i == null) {
i = new IntentBindRecord(this, filter);
bindings.put(filter, i);
}
AppBindRecord a = i.apps.get(app);
if (a != null) {
return a;
}
a = new AppBindRecord(this, i, app);
i.apps.put(app, a);
return a;
}
這裡可以看到bindings裡面儲存了IntentBindRecord記錄。IntentBindRecord儲存的是intent。
requestServiceBindingsLocked通過迴圈取出該記錄,執行requestServiceBindingLocked:
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
if (r.app == null || r.app.thread == null) {
// If service is not currently running, can't yet bind.
return false;
}
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r, execInFg, "bind");
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
//呼叫客戶端的scheduleBindService方法
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
if (!rebind) {
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
} catch (TransactionTooLargeException e) {
// Keep the executeNesting count accurate.
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r, e);
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
throw e;
} catch (RemoteException e) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Crashed while binding " + r);
// Keep the executeNesting count accurate.
final boolean inDestroying = mDestroyingServices.contains(r);
serviceDoneExecutingLocked(r, inDestroying, inDestroying);
return false;
}
}
return true;
}
這裡值得注意的是:
1. startService流程也會執行到requestServiceBindingsLocked方法,可是由於沒有給bindings賦值,所以不會執行迴圈裡的requestServiceBindingLocked方法。即不會執行onBind方法
2. bindService流程執行requestServiceBindingsLocked後,會繼續執行sendServiceArgsLocked方法,通過startService流程解析,我們知道該方法後續會執行onStartCommend。執行onStartCommend的標識是pendingStarts這個記錄。在前文中有提到記錄如何賦值的。bindService流程並沒有給其賦值。所以bindService並不會執行onStartCommend方法。
我們再看requestServiceBindingLocked方法又開始執行客戶端ActivityThread中的方法:scheduleBindService:
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
s.token = token;
s.intent = intent;
s.rebind = rebind;
if (DEBUG_SERVICE)
Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="
+ Binder.getCallingUid() + " pid=" + Binder.getCallingPid());
sendMessage(H.BIND_SERVICE, s);
}
ServiceConnection的onServiceConnected流程
後面的流程和startService在客戶端流程一致,會呼叫到ActivityThread中的方法handleBindService:
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (DEBUG_SERVICE)
Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
if (!data.rebind) {
//執行客戶端-server的onBind方法,並返回IBinder
IBinder binder = s.onBind(data.intent);
//注意:又一個AMS流程,用返回的IBinder呼叫客戶端-client的onServiceConnected
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
ensureJitEnabled();
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to bind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}
這裡相比startService不同的是:ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder);方法。該方法其實又是AMS流程,最終呼叫客戶端-client的onServiceConnected方法。
那麼我們看下具體是怎麼實現的吧。經過一系列呼叫,最終會執行ActiveServices的publishServiceLocked方法:
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
final long origId = Binder.clearCallingIdentity();
try {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "PUBLISHING " + r
+ " " + intent + ": " + service);
if (r != null) {
Intent.FilterComparison filter
= new Intent.FilterComparison(intent);
IntentBindRecord b = r.bindings.get(filter);
if (b != null && !b.received) {
b.binder = service;
b.requested = true;
b.received = true;
for (int conni=r.connections.size()-1; conni>=0; conni--) {
ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
for (int i=0; i<clist.size(); i++) {
ConnectionRecord c = clist.get(i);
if (!filter.equals(c.binding.intent.intent)) {
if (DEBUG_SERVICE) Slog.v(
TAG_SERVICE, "Not publishing to: " + c);
if (DEBUG_SERVICE) Slog.v(
TAG_SERVICE, "Bound intent: " + c.binding.intent.intent);
if (DEBUG_SERVICE) Slog.v(
TAG_SERVICE, "Published intent: " + intent);
continue;
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Publishing to: " + c);
try {
//關鍵程式碼
c.conn.connected(r.name, service);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + r.name +
" to connection " + c.conn.asBinder() +
" (in " + c.binding.client.processName + ")", e);
}
}
}
}
serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
關鍵程式碼是c.conn.connected(r.name, service);這裡的c是一個ConnectionRecord。是從ServiceRecord中的connections獲取的。connections是在哪裡賦值的呢?其實還是在一開始的bindServiceLocked方法裡:
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String callingPackage, final int userId) throws TransactionTooLargeException {
...
//生成ConnectionRecord作為value
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();//獲取 InnerConnection,作為key
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
if (clist == null) {
clist = new ArrayList<ConnectionRecord>();
s.connections.put(binder, clist);//給connections賦值
}
clist.add(c);
....
return 1;
}
注意這裡的connection引數,其實它就是文章一開始說的InnerConnection。
ConnectionRecord類儲存了該connection。最終在publishServiceLocked裡面迴圈取出並呼叫connection的connected方法。文章開頭已經知道該方法最終會執行ServiceConnection的onServiceConnected方法。
至此bindService整個流程結束。
總結:
- startService和bindService在AMS中最後都會執行到realStartServiceLocked方法。
- realStartServiceLocked中會依次執行requestServiceBindingsLocked、sendServiceArgsLocked方法
- requestServiceBindingsLocked:通過迴圈IntentBindRecord記錄來執行onBind方法
- sendServiceArgsLocked:通過迴圈pendingStarts記錄來執行onStartCommend方法
- IntentBindRecord記錄在bindServiceLocked中賦值
- pendingStarts(StartItem)記錄在startServiceLocked中賦值
- bindService流程執行onBind後,會比startService流程多一步,呼叫onServiceConnected方法,該步驟也是一個AMS呼叫過程。用到了ServiceDispatch類。