通過startService只能是把Service給啓動起來,但是我們無法與其建立聯繫.通過bindService方式啓動Service的話,不僅能啓動Service,還能與其建立連接,相互調用比較方便.今天我們來理一理bindService其中的原理.
建議先看一下如下兩篇文章,我按照順序來寫的,循序漸進.可能有些東西前面已經介紹了,後面就不再贅述,感謝理解.
1. 使用方式
簡單回顧一下使用方式,就是在Activity裏面調一下bindService方法,需要傳入一個ServiceConnection.
Intent intentService = new Intent(this,MyService.class);
bindService(intentService,mServiceConnection,BIND_AUTO_CREATE);
2. 源碼分析
和startService類似,bindService也是調用的ContextImpl裏面的方法.
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
...
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
IServiceConnection sd;
//注意 ServiceConnection不能傳入null,否則直接拋個異常給你
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (mPackageInfo != null) {
//會走到這裏來 生成一個IServiceConnection,它其實是一個ServiceDispatcher.InnerConnection,用來與Service建立連接的,與Service建立連接時可能會需要遠程調用,那麼ServiceConnection是不得行的.就需要ServiceDispatcher.InnerConnection來遠程調用,然後再通知ServiceConnection.
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
} else {
throw new RuntimeException("Not supported in system context");
}
validateServiceIntent(service);
try {
//獲取當前Activity的token
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;
}
service.prepareToLeaveProcess(this);
//好巧 又是我們所熟悉的AMS bindService也需要AMS參與
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();
}
}
bindService是ContextImpl裏面的方法,最終會調用到bindServiceCommon方法.bindServiceCommon方法首先將ServiceConnection轉換成了一個ServiceDispatcher.InnerConnection(也就是上面的IServiceConnection sd).我們知道,綁定Service可能是跨進程的,所以需要跨進程通信,這裏使用的是Binder方式.而這裏的IServiceConnection其實是一個ServiceDispatcher.InnerConnection,而這裏的ServiceDispatcher.InnerConnection是拿來跨進程通信的,因爲ServiceConnection不能直接進行跨進程通信,所以需要ServiceDispatcher.InnerConnection來充當Binder的角色.然後ServiceDispatcher是連接InnerConnection和ServiceConnection的,InnerConnection是ServiceDispatcher的一個內部類.
上面的mPackageInfo是LoadedApk,跟進去看看getServiceDispatcher方法
public final IServiceConnection getServiceDispatcher(ServiceConnection c,
Context context, Handler handler, int flags) {
synchronized (mServices) {
//ServiceDispatcher是LoadedApk的內部類
LoadedApk.ServiceDispatcher sd = null;
//mServices是一個map,定義是private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
= new ArrayMap<>();
//它裏面存放的是當前Activity與ServiceConnection和ServiceDispatcher的映射關係
//如果之前有創建好的ServiceDispatcher,那麼直接拿出來用,沒有則創建一個
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
if (map != null) {
sd = map.get(c);
}
if (sd == null) {
//創建一個ServiceDispatcher
sd = new ServiceDispatcher(c, context, handler, flags);
if (map == null) {
map = new ArrayMap<>();
//放入mServices進行緩存起來
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler);
}
//獲取ServiceDispatcher中的InnerConnection對象
return sd.getIServiceConnection();
}
}
首先ServiceDispatcher是LoadedApk的內部類.系統將當前Activity與ServiceConnection和ServiceDispatcher的映射關係緩存起來了的,有需要的時候直接拿出來.當前第一次綁定的時候,肯定緩存起來沒有,所以需要創建一個ServiceDispatcher.創建ServiceDispatcher的時候就會創建InnerConnection. 相當於ServiceDispatcher內部有ServiceConnection和InnerConnection,那麼後面需要調用ServiceConnection裏面的方法就比較方便了.
接着我們繼續看AMS的綁定Service的過程,是調用的是AMS的bindService方法
public int bindService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String callingPackage,
int userId) throws TransactionTooLargeException {
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, callingPackage, userId);
}
mServices是ActiveServices,在startService源碼分析中提到過,ActiveServices類是輔助AMS管理Service的,包括Service的啓動、綁定和停止等.
上面是調用了ActiveServices的bindServiceLocked方法,bindServiceLocked再調
用bringUpServiceLocked,bringUpServiceLocked又會調用realStartServiceLocked方法,
realStartServiceLocked方法的執行邏輯和"死磕Android_Service啓動流程分析"中的邏輯類似,最終都是通過
ApplicationThread來完成Service實例的創建並執行其onCreate方法,這裏不再重複講解了.
但是需要注意的是在realStartServiceLocked方法裏面,當我們是bindService綁定Service的時候,需要關注一個東西
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
boolean created = false;
//啓動Service
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
created = true;
//深入
requestServiceBindingsLocked(r, execInFg);
......
}
private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)
throws TransactionTooLargeException {
//深入
requestServiceBindingLocked(r, ibr, execInFg, false);
}
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
//遠程調用,執行ActivityThread中的scheduleBindService方法
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
return true;
}
在realStartServiceLocked方法裏面調用了requestServiceBindingsLocked方法,requestServiceBindingsLocked方法調用了另一個requestServiceBindingLocked方法,然後遠程調用ActivityThread的scheduleBindService方法.scheduleBindService方法從名字看,終於要開始執行綁定了
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
//注意 這裏將Activity的token傳過來了
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);
}
然後又來到了我們熟悉的H這個Handler,在H中BIND_SERVICE消息調用的是ActivityThread中的handleBindService()這個方法
private void handleBindService(BindServiceData data) {
//在handleCreateService方法裏面將token和Service映射存入了mServices裏面
//mServices定義是final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
//所以這裏可以根據token取出Service
Service s = mServices.get(data.token);
if (s != null) {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
if (!data.rebind) {
//如果不是重新綁定
//注意,這裏調用了我們熟悉的Service的onBind方法,,,,就是在這裏調用的哦,,,所以這裏是UI線程哈,記住了
IBinder binder = s.onBind(data.intent);
//調用AMS的publishService方法
ActivityManager.getService().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
}
}
在handleCreateService方法裏面將token和Service映射存入了mServices裏面,mServices定義是final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
.所以那裏可以根據token取出Service,然後緊接着調用了我們熟悉的Service的onBind方法,這裏可以得出onBind方法是在UI線程執行的.還可以得出,重新綁定Service時,onBind方法只會調用一次,除非Service之前被終止了.onBind方法執行之後,說明Service已經成功連接了.
然後又來到了AMS,這次是AMS的publishService方法.
public void publishService(IBinder token, Intent intent, IBinder service) {
mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}
//ActiveServices.java => publishServiceLocked
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
try {
if (r != null) {
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);
//核心代碼
c.conn.connected(r.name, service, false);
}
}
serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
}
} finally {
Binder.restoreCallingIdentity(origId);
}
}
c.conn的類型是ServiceDispatcher.InnerConnection,service就是Service
的onBind方法返回的Binder對象.來看一下ServiceDispatcher.InnerConnection的connected方法
private static class InnerConnection extends 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,所以可以很輕鬆拿到ServiceDispatcher,拿到ServiceDispatcher調用其connected方法
public void connected(ComponentName name, IBinder service, boolean dead) {
//mActivityThread其實就是ActivityThread中名爲H的Handler
if (mActivityThread != null) {
//主線程中的Handler調用post 說明RunConnection是運行在主線程中
mActivityThread.post(new RunConnection(name, service, 0, dead));
} else {
doConnected(name, service, dead);
}
}
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;
}
//ServiceDispatcher => doConnected
public void doConnected(ComponentName name, IBinder service, boolean dead) {
...
//mConnection是ServiceDispatcher中的ServiceConnection,初始化ServiceDispatcher的時候就初始化了ServiceConnection
mConnection.onServiceConnected(name, service);
...
}
通過ActivityThread中的Handler#post執行一個doConnected方法,而doConnected方法裏面就是通過我們熟悉的ServiceConnection對象進行了onServiceConnected方法的回調.
可以看到ServiceDispatcher做了一個轉接.當Service連接上之後,通過InnerConnection去遠程調用ServiceDispatcher中的ServiceConnection中的onServiceConnected方法,完成綁定成功消息的通知.讓客戶端知道Service已綁定.
bindService也就分析完成了,其他比如
Service的停止過程和解除綁定的過程,系統的執行過程是類似的,這裏留給大家自己去分析咯.