Service源码浅析一
Service源码浅析二
Service源码浅析三
Service源码浅析四
今天又发现两个问题,平时开发中的细节太多了,可能很多只是知道结论,不知道原理,争取以后一一记录下来,探究原理。
1. 主动unbindService会触发onServiceDisconnected回调吗?
先说答案,不会回调onServiceDisconnected。
AMS#unbindService -> ActiveServices#unbindServiceLocked -> removeConnectionLocked -> ActivityThread#scheduleUnbindService -> handleUnbindService -> Service#onUnbind -> AMS#unbindFinished -> ActiveServices#unbindFinishedLocked
回到ActiveServices#removeConnectionLocked -> removeConnectionLocked -> bringDownServiceIfNeededLocked -> bringDownServiceLocked -> ActivityThread#scheduleStopService -> Service#onDestroy
这是咱们整理的unbindService的流程,现在咱们就要在这堆流程中找出第一个问题的答案。
-->ActivityManagerService
public boolean unbindService(IServiceConnection connection) {
try {
if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "unbindService");
}
synchronized (this) {
return mServices.unbindServiceLocked(connection);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
触发ActivityManagerService#unbindService时传入了IServiceConnection,一会咱们看看这个IServiceConnection会起什么作用。
ActiveServices#unbindServiceLocked中会根据IServiceConnection取出对应的ConnectionRecord
/**
* All currently bound service connections. Keys are the IBinder of
* the client's IServiceConnection.
*/
final ArrayMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections = new ArrayMap<>();
ConnectionRecord是在绑定服务流程bindServiceLocked时创建的,它持有IServiceConnection的引用
/**
* Description of a single binding to a service.
*/
final class ConnectionRecord implements OomAdjusterModernImpl.Connection{
final AppBindRecord binding; // The application/service binding.
final ActivityServiceConnectionsHolder<ConnectionRecord> activity; // If non-null, the owning activity.
final IServiceConnection conn; // The client connection.
...
}
接下来进入ActiveServices#removeConnectionLocked
int removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
ActivityServiceConnectionsHolder skipAct, boolean enqueueOomAdj) {
IBinder binder = c.conn.asBinder();
AppBindRecord b = c.binding;
ServiceRecord s = b.service;
@ServiceBindingOomAdjPolicy int serviceBindingOomAdjPolicy =
SERVICE_BIND_OOMADJ_POLICY_LEGACY;
ArrayList<ConnectionRecord> clist = s.getConnections().get(binder);
if (clist != null) {
clist.remove(c);
if (clist.size() == 0) {
s.removeConnection(binder);
}
}
...
在这里根据ConnectionRecord获取了ServiceRecord,ServiceRecord中有对应的
private final ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections
= new ArrayMap<IBinder, ArrayList<ConnectionRecord>>();
在这个方法中触发了
s.app.getThread().scheduleUnbindService(s, b.intent.intent.getIntent());
传入了ServiceRecord,我们现在改为追踪ServiceRecord。
private void handleUnbindService(BindServiceData data) {
CreateServiceData createData = mServicesData.get(data.token);
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
data.intent.prepareToEnterProcess(isProtectedComponent(createData.info),
s.getAttributionSource());
boolean doRebind = s.onUnbind(data.intent);
try {
if (doRebind) {
ActivityManager.getService().unbindFinished(
data.token, data.intent);
} else {
ActivityManager.getService().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_UNBIND, 0, 0, data.intent);
}
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
"Unable to unbind to service " + s
+ " with " + data.intent + ": " + e.toString(), e);
}
}
}
}
进入Service进程的ActivityThread#handleUnbindService,这里又触发了Service#onUnbind
/**
* Called when all clients have disconnected from a particular interface
* published by the service. The default implementation does nothing and
* returns false.
*
* @param intent The Intent that was used to bind to this service,
* as given to {@link android.content.Context#bindService
* Context.bindService}. Note that any extras that were included with
* the Intent at that point will <em>not</em> be seen here.
*
* @return Return true if you would like to have the service's
* {@link #onRebind} method later called when new clients bind to it.
*/
public boolean onUnbind(Intent intent) {
return false;
}
如果onUnbind返回false,咱们执行AMS#serviceDoneExecuting
@Override
public void serviceDoneExecuting(IBinder token, int type, int startId, int res, Intent intent) {
synchronized(this) {
if (!(token instanceof ServiceRecord)) {
Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token);
throw new IllegalArgumentException("Invalid service token");
}
mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false,
intent);
}
}
传入的token就是触发ActivityThread#handleUnbindService时传入的ServiceRecord。
走到这里有点不太对劲啊,后续好像没有执行Service#onDestroy的逻辑,也就是说如果Service#onUnbind返回false,后续不会走onDestroy吗?
咱们重新回到ActiveServices#removeConnectionLocked在方法最后有这样一段逻辑
int removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
ActivityServiceConnectionsHolder skipAct, boolean enqueueOomAdj) {
...
if (c.hasFlag(Context.BIND_AUTO_CREATE)) {
boolean hasAutoCreate = s.hasAutoCreateConnections();
if (!hasAutoCreate) {
if (s.tracker != null) {
synchronized (mAm.mProcessStats.mLock) {
s.tracker.setBound(false, mAm.mProcessStats.getMemFactorLocked(),
SystemClock.uptimeMillis());
}
}
}
bringDownServiceIfNeededLocked(s, true, hasAutoCreate, enqueueOomAdj,
"removeConnection");
}
...
}
如果启动Service时指定了BIND_AUTO_CREATE,则会执行bringDownServiceIfNeededLocked,如果没有指定这个Flag,说明Service不是自动创建的,不需要销毁,这样逻辑就圆起来了啊,完美。
咱们继续顺着bringDownServiceIfNeededLocked往下走,跟紧ServiceRecord。
接着调用bringDownServiceLocked,这是一个超级方法。
// Report to all of the connections that the service is no longer
// available.
ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections = r.getConnections();
for (int conni = connections.size() - 1; conni >= 0; conni--) {
ArrayList<ConnectionRecord> c = connections.valueAt(conni);
for (int i=0; i<c.size(); i++) {
ConnectionRecord cr = c.get(i);
// There is still a connection to the service that is
// being brought down. Mark it as dead.
cr.serviceDead = true;
cr.stopAssociation();
final ComponentName clientSideComponentName =
cr.aliasComponent != null ? cr.aliasComponent : r.name;
try {
cr.conn.connected(clientSideComponentName, null, true);
} catch (Exception e) {
Slog.w(TAG, "Failure disconnecting service " + r.shortInstanceName
+ " to connection " + c.get(i).conn.asBinder()
+ " (in " + c.get(i).binding.client.processName + ")", e);
}
}
}
cr.conn.connected(clientSideComponentName, null, true);会调到客户端的LoadedApk
private static class InnerConnection extends IServiceConnection.Stub {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
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);
}
}
}
入参dead=true,接着触发ServiceDispatcher#doConnected
public void doConnected(ComponentName name, IBinder service, boolean dead) {
ServiceDispatcher.ConnectionInfo old;
ServiceDispatcher.ConnectionInfo info;
synchronized (this) {
if (mForgotten) {
// We unbound before receiving the connection; ignore
// any connection received.
return;
}
old = mActiveConnections.get(name);
if (old != null && old.binder == service) {
// Huh, already have this one. Oh well!
return;
}
if (service != null) {
// A new service is being connected... set it all up.
info = new ConnectionInfo();
info.binder = service;
info.deathMonitor = new DeathMonitor(name, service);
try {
service.linkToDeath(info.deathMonitor, 0);
mActiveConnections.put(name, info);
} catch (RemoteException e) {
// This service was dead before we got it... just
// don't do anything with it.
mActiveConnections.remove(name);
return;
}
} else {
// The named service is being disconnected... clean up.
mActiveConnections.remove(name);
}
if (old != null) {
old.binder.unlinkToDeath(old.deathMonitor, 0);
}
}
// If there was an old service, it is now disconnected.
if (old != null) {
mConnection.onServiceDisconnected(name);
}
if (dead) {
mConnection.onBindingDied(name);
} else {
// If there is a new viable service, it is now connected.
if (service != null) {
mConnection.onServiceConnected(name, service);
} else {
// The binding machinery worked, but the remote returned null from onBind().
mConnection.onNullBinding(name);
}
}
}
这个方法之前贴过,这里在贴一遍,可以看到如果old不为null,会触发onServiceDisconnected,
private final ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
= new ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
old是从mActiveConnections中取出的,
void doForget() {
synchronized(this) {
for (int i=0; i<mActiveConnections.size(); i++) {
ServiceDispatcher.ConnectionInfo ci = mActiveConnections.valueAt(i);
ci.binder.unlinkToDeath(ci.deathMonitor, 0);
}
mActiveConnections.clear();
mForgotten = true;
}
}
mActiveConnections在最开始调用forgetServiceDispatcher时就触发了ServiceDispatcher#doForget清空了mActiveConnections,所以正常解绑流程old是null的,也就是不会触发onServiceDisconnected,异常情况,服务端崩溃导致的调用old不为null则触发onServiceDisconnected,到这里其实这个问题已经破案了,bringDownServiceLocked后面就是destroy Service的逻辑了,不详细分析了。
2. unbindService一个未绑定的服务,会发生异常吗?
答案是会的,不管是服务未绑定过还是绑定过但是解绑了,都会触发异常。
---> ContextImpl
@Override
public void unbindService(ServiceConnection conn) {
if (conn == null) {
throw new IllegalArgumentException("connection is null");
}
if (mPackageInfo != null) {
IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
getOuterContext(), conn);
try {
ActivityManager.getService().unbindService(sd);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
throw new RuntimeException("Not supported in system context");
}
}
unbindService的主流程咱们在之前的文章中跟过,但是主流程好过,流程中夹杂的细节就不太好办了,只能通过问题的方式一点一点的扣,这个问题就隐藏在流程中。
这里触发了mPackageInfo.forgetServiceDispatcher,其中mPackageInfo是LoadedApk的实例。
public final IServiceConnection forgetServiceDispatcher(Context context,
ServiceConnection c) {
synchronized (mServices) {
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map
= mServices.get(context);
LoadedApk.ServiceDispatcher sd = null;
if (map != null) {
sd = map.get(c);
if (sd != null) {
if (DEBUG) Slog.d(TAG, "Removing dispatcher " + sd + " for conn " + c);
map.remove(c);
sd.doForget();
if (map.size() == 0) {
mServices.remove(context);
}
if ((sd.getFlags()&Context.BIND_DEBUG_UNBIND) != 0) {
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
= mUnboundServices.get(context);
if (holder == null) {
holder = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
mUnboundServices.put(context, holder);
}
RuntimeException ex = new IllegalArgumentException(
"Originally unbound here:");
sd.setUnbindLocation(ex);
holder.put(c, sd);
}
return sd.getIServiceConnection();
}
}
ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
= mUnboundServices.get(context);
if (holder != null) {
sd = holder.get(c);
if (sd != null) {
RuntimeException ex = sd.getUnbindLocation();
throw new IllegalArgumentException(
"Unbinding Service " + c
+ " that was already unbound", ex);
}
}
if (context == null) {
throw new IllegalStateException("Unbinding Service " + c
+ " from Context that is no longer in use: " + context);
} else {
throw new IllegalArgumentException("Service not registered: " + c);
}
}
}
这段代码的逻辑比较简单,其中mServices和mUnboundServices是两个map
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
= new ArrayMap<>();
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
= new ArrayMap<>();
如果是正常解绑,就返回ServiceDispatcher#getIServiceConnection这里的InnerConnection之前也提到过,Service解绑/绑定之后会触发它的回调方法。
mServices和mUnboundServices分别在绑定和解绑时进行更新。