一. 前言
我们可以通过startService来启动一个服务, 当然也可以通过bindService绑定一个服务,本篇文章我们来讲一讲绑定服务的完整流程, 阅读此文之前,建议先阅读一下笔者的这三篇文章 Android 进程间通信机制(三) 系统进程与应用进程通信
Android 进程间通信机制(五) startService流程
整个流程我们从 应用进程到AMS的的调用过程 和 Sevice的绑定过程 来梳理一下
二. 流程分析
绑定服务,一般流程在客户端调用bindService()方法, 待绑定服务端的service成功后, 再回调客户端的ServiceConnection中的onServiceConnected()方法. 好了,接下来就一起看看代码
预先准备:自己写了一个客户端MyClient.apk 一个服务端MyService.apk
2.1 App ---> AMS
比如客户端MyClient.apk 调用代码如下:
Intent intent = new Intent();
ComponentName componentName = new ComponentName("com.example.mysevicejava",
"com.example.mysevicejava.MyService");
intent.setComponent(componentName);
this.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
第一个参数传入Intent,第二个参数传入创建的ServiceConnection, ServiceConnection本身是一个接口. 其中包括两个回调方法, bindService()是在ContextWrapper.java类中实现的。
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
return mBase.bindService(service, conn, flags);
}
mBase是ContextImpl类型对象,调用ContextImpl的bindService()方法。
@Override
public boolean bindService(
Intent service, int flags, Executor executor, ServiceConnection conn) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, null, null, executor, getUser());
}
继续调用ContextImpl.java中的 bindServiceCommon 方法
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
String instanceName, Handler handler, Executor executor, UserHandle user) {
....
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
// 先提前说一下, IServiceConnection 也是一个接口,继承android.os.IInterface 说明是一个Binder,用于跨进程通信用的, 后文有用到它的地方
IServiceConnection sd;
if (mPackageInfo != null) {
//注释1
if (executor != null) {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), executor, flags);
} else {
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
}
} else {
throw new RuntimeException("Not supported in system context");
}
//检查service intent的有效性, 在高于L版本之后, 启动servie必须是显示的intent
//要带上包名和类名
validateServiceIntent(service);
//注释2
int res = ActivityManager.getService().bindIsolatedService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, instanceName, getOpPackageName(), user.getIdentifier());
}
注释1处创建sd对象
注释2处调用bindIsolatedService()方法与系统进程中的AMS交互
在注释1处调用了 LoadedApk类型的对象 mPackagelnfo 的getServiceDispatcher方法.
接着又调用 getServiceDispatcherCommon这个方法
private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,
Context context, Handler handler, Executor executor, int flags) {
.....
LoadedApk.ServiceDispatcher sd = null;
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
//第一次绑定sd为null executor为null
if (sd == null) {
if (executor != null) {
sd = new ServiceDispatcher(c, context, executor, flags);
} else {
//走这里的代码,创建一个ServiceDispatcher实例。
sd = new ServiceDispatcher(c, context, handler, flags);
}
//创建好后, 用map存起来 key为 ServiceConnection对象(客户端传递过来的ServiceConnection), value为ServiceDispatcher对象
map.put(c, sd);
//返回一个InnerConnection对象。
return sd.getIServiceConnection();
}
在来看看 ServiceDispatcher这个类, 首先它属于LoadedApk.java中的一个静态类
/frameworks/base/core/java/android/app/LoadedApk.java
static final class ServiceDispatcher {
private final ServiceDispatcher.InnerConnection mIServiceConnection;
.....
private static class ConnectionInfo {
IBinder binder;
IBinder.DeathRecipient deathMonitor;
}
//注释1 内部类 InnerConnection是一个Binder
//看见这块比较熟悉的代码,在跨进程通信来看, 这里就属于服务端的代码, 那谁是客户端呢?
//答案是 AMS
private static class InnerConnection extends IServiceConnection.Stub {
@UnsupportedAppUsage
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);
}
}
}
//ServiceDispatcher构造方法
@UnsupportedAppUsage
ServiceDispatcher(ServiceConnection conn,
Context context, Handler activityThread, int flags) {
//创建一个InnerConnection对象,是一个IBinder
mIServiceConnection = new InnerConnection(this);
//将客户端conn赋值给mConnection
mConnection = conn;
mContext = context;
mActivityThread = activityThread;
mActivityExecutor = null;
mLocation = new ServiceConnectionLeaked(null);
mLocation.fillInStackTrace();
mFlags = flags;
}
IServiceConnection getIServiceConnection() {
return mIServiceConnection;
}
.....
}
在看看IServiceConnection.aidl 文件, 用关键字oneway修饰,说明在远程调用时(是异步调用,即客户端AMS不会被阻塞), 它只是发送事务数据并立即返回, oneway修饰了的方法不可以有返回值.
/** @hide */
oneway interface IServiceConnection {
@UnsupportedAppUsage
void connected(in ComponentName name, IBinder service, boolean dead);
}
上面这段代码IServiceConnection 跨进程通信模型如下:
IServiceConnection 用于在 AMS 在bindService成功后通知app(此时AMS作为客户端, app进程作为服务端), 然后回调app的 ServiceConnection的 onServiceConnected方法.
好了把注释1 和 注释2 分析完后, 先总结一下时序图:
2.2 Sevice绑定过程
接下来就进入到 AMS 中
调用到ActivityManagerService的bindIsolatedService方法
public int bindIsolatedService(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, IServiceConnection connection, int flags, String instanceName,
String callingPackage, int userId) throws TransactionTooLargeException {
....
synchronized(this) {
return mServices.bindServiceLocked(caller, token, service,
resolvedType, connection, flags, instanceName, callingPackage, userId);
}
....
}
接下来调用 frameworks/base/services/core/java/com/android/server/am/ActiveServices.java
中的 bindServiceLocked 方法:
int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,
String resolvedType, final IServiceConnection connection, int flags,
String instanceName, String callingPackage, final int userId)
throws TransactionTooLargeException {
.....
ServiceRecord s = res.record;
boolean permissionsReviewRequired = false;
.....
//注释1记录需要bind的service
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent,
callerApp.uid, callerApp.processName, callingPackage);
//注释2 这个就是 bindservice()方法的第三个参数 Context.BIND_AUTO_CREATE
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
s.lastActivity = SystemClock.uptimeMillis();
//会调用到service服务端进程进行onCreate onBind
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,
permissionsReviewRequired) != null) {
return 0;
}
}
.....
// 注释3 s为ServiceRecord s.app为ProcessRecord 第一次绑定是s.app为null
// b.intent.received 为false 所以走else分支.
if (s.app != null && b.intent.received) {
// Service is already running, so we can immediately
// publish the connection.
//直接翻译英文注释: 服务已经正在运行, 所以我们立马能回调 connection
//即回调 IServiceConnection 接口中的 connected(3个参数)方法了
try {
c.conn.connected(s.name, b.intent.binder, false);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + s.shortInstanceName
+ " 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.
// 注释4 应用进程与服务进行绑定,并且服务已经调用过onUnbind方法
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
//注释5 如果应用进程没有发送过绑定service的请求, 第一次绑定会走这里
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
.....
}
先看注释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;
}
讲到这 有必要先介绍几个与 Service 关的对象类型 ,这样有助于对源码进行理解,
如下所示
ServiceRecord :用于描述一个 Service
ProcessRecord : 一个进程的信息。
ConnectionRecord :用于描述应用进程和 Service 建立的一次通信。
AppBindRecord :应用进程通过 Intent 绑定 Service 时,会通过 AppBindRecord
来维护 Service 与应用程序进程之间的关联。其内部存储了谁绑定的 Service
( ProcessRecord 、被绑定的 Service ( AppBindRecord )、绑定 Service Intent
( IntentBindRecord )和所有绑定通信记 的信息( ArraySet<Connect onRecord )。
IntentBindRecord :用于描述绑定 Service Intent
注释2处部分
在注释2处调用 bringUpServiceLocked 方法,在 bringUpServiceLocked 方法中又调用
realStartServiceLocked 方法,最终由 ActivityThread 来调用 Service onCreate 方法启动
Service ,这也说明了 bindService 方法内部会启动 Service
关于startService的启动流程 请查阅笔者的 Android 进程间通信机制(五) startService流程
注释3处部分:
s为ServiceRecord s.app为ProcessRecord 第一次绑定是s.app为null b.intent.received 为false 所以走else分支(走注释5处代码)
b.intent.received只有在服务端MyService执行 onBind()并且publishService()之后才为true
s.app是在realStartServiceLocked()的时候被赋值.
注释4处表示:应用进程与服务进行绑定,并且服务已经调用过onUnbind方法
继续分析 注释5 部分
注释5处表示: 如果应用进程没有发送过绑定service的请求,第一次绑定就会走这个分支
private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,
boolean execInFg, boolean rebind) throws TransactionTooLargeException {
if ((!i.requested || rebind) && i.apps.size() > 0) {
try {
bumpServiceExecutingLocked(r, execInFg, "bind");
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
//这里就通过IApplicationThread进行跨进程通信, 跳转到app进程的 ActivityThread.java的scheduleBindService方法了
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.getReportedProcState());
if (!rebind) {
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
}
继续跳转到
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());
//发送BIND_SERVICE消息
sendMessage(H.BIND_SERVICE, s);
}
//处理消息的代码:
case BIND_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
handleBindService((BindServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
....
继续调用frameworks/base/core/java/android/app/ActivityThread java的handleBindService方法
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token); //1
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess();
try {
if (!data.rebind) { //2
IBinder binder = s.onBind(data.intent); //3
ActivityManager.getService().publishService(
data.token, data.intent, binder); //4
} else {
s.onRebind(data.intent);
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
} 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);
}
}
....
}
在1 处获取要绑定的 Service
在2处BindServiceData成员变量rebind值为false
在3处,就执行来调用 Service中的 onBind方法, 比如我自己写MyService.java
@Override
public IBinder onBind(Intent intent) {
Log.e("test", "==服务端==onBind========");
// TODO: Return the communication channel to the service.
return new MyBinder();
}
如果 rebind 的值为 true 则调用 5处的 Service onRebind 方法
在4会调用
ActivityManager.getService()的publishService方法,调用ActivityManagerService的publishService()方法。
public void publishService(IBinder token, Intent intent, IBinder service) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
throw new IllegalArgumentException("Invalid service token");
}
mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}
}
接着又会调用 mServices.publishServiceLocked((ServiceRecord)token, intent, service);
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;
ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> clist = 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, false);
} catch (Exception e) {
Slog.w(TAG, "Failure sending service " + r.shortInstanceName
+ " to connection " + c.conn.asBinder()
+ " (in " + c.binding.client.processName + ")", e);
}
重点关注 c.conn.connected(r.name, service, false);
可以再回过头看下2.1 小结中的 IServiceConnection 通信的示意图(见2.1小结的分析图)
调用 c.conn.connected方法,其中 c.conn指的是 IServiceConnection , 它的实现为ServiceDispatcher.InnerConnection ,实现代码在 /frameworks/base/core/java/android/app/LoadedApk.java 中
其中ServiceDispatcher 是LoadedApk 的内部类, 而InnerConnection又是ServiceDispatcher的内部类.
c.conn.connected(xxx) 就调用的如下的代码
private static class InnerConnection extends IServiceConnection.Stub {
@UnsupportedAppUsage
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);
}
}
}
继续调用 sd.connected(name, service, dead)方法:
public void connected(ComponentName name, IBinder service, boolean dead) {
if (mActivityExecutor != null) {
mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
} else if (mActivityThread != null) {
//注释1
mActivityThread.post(new RunConnection(name, service, 0, dead));
} else {
doConnected(name, service, dead);
}
}
注释1处调用 Handler 类型的对象 mActivityThread post 方法, mActivityThread
际上指向的是H 。通过调用H 的post方法将 RunConnection 对象的内容运行在主线
程中。 下面再看看RunConnection 里面的run方法
/frameworks/base/core/java/android/app/LoadedApk.java
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() {
//上面传递进来的第三个参数 mCommand 为 0,所以走这里的代码
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(mName, mService, mDead)方法
public void doConnected(ComponentName name, IBinder service, boolean dead) {
.....
// 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 viable service, it is now connected.
//这里就回调到了 客户端的 onServiceConnected 方法了
if (service != null) {
mConnection.onServiceConnected(name, service);
} else {
// The binding machinery worked, but the remote returned null from onBind().
mConnection.onNullBinding(name);
}
.....
}
这样在客户端实现了 ServiceConnection 接口类的 onServiceConnected方法就会被执
行。至此,Service 的绑定过程就分析完成。
三. 整体流程图
在自己写的demo(客户端 MyClient.apk 服务端MyService.apk)中加入log,也和上面分析的流程图是一致的
03-21 21:50:57.942 4221 9156 E test : =ActiveServices===创建==scheduleCreateService==
03-21 21:50:57.943 4221 9156 E test : ===ActiveServices scheduleBindService===
03-21 21:50:57.945 29708 29708 E test : =服务端MyService==onCreate=====
03-21 21:50:57.946 29708 29708 E test : ==服务端MyService==onBind========
03-21 21:50:57.964 29831 29831 E test : 客户端MyClient 回调方法 onServiceConnected