前言:
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();