Android Service演义
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Service演义相关的知识,希望对你有一定的参考价值。
摘要: 本文基于android 5.1代码,介绍了Android Service的运作机理。按理说,网上此类文章已经很多了,本不需我再赘述。但每个人理解技术的方式多少会有所不同,我多写一篇自己理解的service,也未尝不可吧。
Android Service演义
(本文以Android 5.1为准)
侯亮
1.概述
在Android平台上,那种持续性工作一般都是由service来执行的。不少初学者总是搞不清service和线程、进程之间的关系,这当然会影响到他们开展具体的开发工作。
其实,简单说起来,service和线程、进程是没什么关系的。我们知道,在Android平台上已经大幅度地弱化了进程的概念,取而代之的是一个个有意义的逻辑实体,比如activity、service等。Service实体必然要寄身到某个进程里才行,它也可以再启动几个线程来帮它干活儿。但是,说到底service只是一个逻辑实体、一个运行期上下文而已。
相比activity这种“操控UI界面的运行期上下文”,service这种上下文一般是没有界面部分的。当然这里说的只是一般情况,有些特殊的service还是可以创建自己的界面的,比如当一个service需要显现某种浮动面板时,就必须自己创建、销毁界面了。
在Android系统内部的AMS里,是利用各种类型的Record节点来管理不同的运行期上下文的。比如以ActivityRecord来管理activity,以ServiceRecord来管理service。可是,线程这种东东可没有对应的Record节点喔。一些初学者常常会在activity里启动一个线程,从事某种耗时费力的工作,可是一旦activity被遮挡住,天知道它会在什么时候被系统砍掉,进而导致连应用进程也退出。从AMS的角度来看,它压根就不知道用户进程里还搞了个工作线程在干活儿,所以当它要干掉用户进程时,是不会考虑用户进程里还有没有工作没干完。
但如果是在service里启动了工作线程,那么AMS一般是不会随便砍掉service所在的进程的,所以耗时的工作也就可以顺利进行了。
可是,我们也常常遇到那种“一次性处理”的工作,难道就不能临时创建个线程来干活吗?关于这一点,请大家留意,在Android平台上应对这种情况的较好做法是创建一个IntentService派生类,而后覆盖其onHandleIntent()成员函数。IntentService内部会自动为你启动一个工作线程,并在工作线程里回调onHandleIntent()。当onHandleIntent()返回后,IntentService还会自动执行stopSelf()关闭自己。瞧瞧,多么自动化。
关于service,有两个动作是谁都绕不开的,那就是startService()和bindService()。我们想知道,它们的内部机制到底是怎样的?虽然网上已经有不少文章讲述过这两个动作,但是不同人理解技术的视角往往是不一样的,本文我将以自己的视角来阐述它们。
2.Service机制
我们先来看一下Service类的代码截选:
【frameworks/base/core/java/android/app/Service.java】
public abstract class Service extends ContextWrapper implements ComponentCallbacks2 {
......
......
private ActivityThread mThread = null;
private String mClassName = null;
private IBinder mToken = null;
private Application mApplication = null;
private IActivityManager mActivityManager = null;
private boolean mStartCompatibility = false;
}
Service是个抽象类,它间接继承于Context,其继承关系如下图所示:
看了这张图,请大家务必理解,Service只是个“上下文”(Context)对象而已,它和进程、线程是没什么关系的。
在AMS中,负责管理service的ServiceRecord节点本身就是个binder实体。当AMS向应用进程发出语义,要求其创建service对象时,会把ServiceRecord通过binder机制“传递”给应用进程。这样,应用进程的ActivityThread在处理AMS发来的语义时,就可以得到一个合法的binder代理,这个binder代理最终会被记录在Service对象中,如此一来,Service实体就和系统里的ServiceRecord关联起来了。我们画个图来说明,假如一个应用进程里启动了两个不同的Service,那么当service创建成功之后,AMS和用户进程之间就会形成如下关系示意图:
当然,如果用户进程和service再多一点儿也完全没问题,此时会形成下图:
我们对图中的ApplicationThread并不陌生,它记录在ActivityThread中mAppThread域中。每当系统新fork一个用户进程后,就会自动执行ActivityThread的attach()动作,里面会调用:
final IActivityManager mgr = ActivityManagerNative.getDefault();
. . . . . .
mgr.attachApplication(mAppThread);
. . . . . .
将ApplicationThread对象远程“传递”给AMS,从而让AMS得到一个合法的代理端。而当系统要求用户进程创建service时,就会通过这个合法的代理端向用户进程传递明确的语义。现在我们就从这里开始讲解细节吧。
3.先说startService()
我们先来看启动service的流程。要启动一个service,我们一般是调用startService()。说穿了只是向AMS发起一个请求,导致AMS执行如下的startService()动作:
【frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java】
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, int userId) {
. . . . . .
ComponentName res = mServices.startServiceLocked(caller, service,
resolvedType, callingPid, callingUid, userId);
. . . . . .
return res;
}
}
其中的mServices域是ActiveServices类型的,其startServiceLocked()函数的代码截选如下:
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】
ComponentName startServiceLocked(IApplicationThread caller,
Intent service, String resolvedType,
int callingPid, int callingUid, int userId) {
. . . . . .
// 必须先通过retrieveServiceLocked()找到(或创建)一个ServiceRecord节点
ServiceLookupResult res = retrieveServiceLocked(service, resolvedType,
callingPid, callingUid, userId, true, callerFg);
. . . . . .
ServiceRecord r = res.record;
. . . . . .
r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
r.delayedStop = false;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
final ServiceMap smap = getServiceMap(r.userId);
. . . . . .
. . . . . .
return startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
}
看到了吗?必须先通过retrieveServiceLocked()找到(或创建)一个ServiceRecord节点,而后才能执行后续的启动service的动作。请大家注意,在Android frameworks里有不少这样的动作,基本上都是先创建xxxRecord节点,而后再向目标用户进程传递语义,创建具体的对象。
retrieveServiceLocked()的代码截选如下:
private ServiceLookupResult retrieveServiceLocked(Intent service,
String resolvedType, int callingPid, int callingUid, int userId,
boolean createIfNeeded, boolean callingFromFg) {
ServiceRecord r = null;
. . . . . .
ServiceMap smap = getServiceMap(userId);
final ComponentName comp = service.getComponent();
if (comp != null) {
r = smap.mServicesByName.get(comp);
}
if (r == null) {
Intent.FilterComparison filter = new Intent.FilterComparison(service);
r = smap.mServicesByIntent.get(filter);
}
if (r == null) {
. . . . . .
// 从PKMS处查到ServiceInfo
. . . . . .
ComponentName name = new ComponentName(
sInfo.applicationInfo.packageName, sInfo.name);
. . . . . .
r = smap.mServicesByName.get(name);
if (r == null && createIfNeeded) {
. . . . . .
r = new ServiceRecord(mAm, ss, name, filter, sInfo, callingFromFg, res);
. . . . . .
smap.mServicesByName.put(name, r);
smap.mServicesByIntent.put(filter, r);
. . . . . .
}
. . . . . .
}
if (r != null) {
. . . . . .
return new ServiceLookupResult(r, null);
}
return null;
}
总之就是希望在AMS内部的相关表格里找到对应的ServiceRecord节点,如果找不到,就创建一个新节点,并插入到相应的表格中。
我手头参考的是Android5.1的代码,可以说这张表格大体上就是ServiceMap里的mServicesByName啦。当然,ServiceRecord节点一般还会同时记录到其他表格里,比如mServicesByIntent表格,但现在我们不妨先只考虑mServicesByName,一切以便于理解为上。
事实上,在早期的Android代码(比如Android 2.3版的代码)中,是没有那个ServiceMap的,那时的mServicesByName表格直接位于ActivityManagerService里,而且对应的域名叫作mServices。后来随着Android的发展,需要支持多用户以及其他一些概念,于是就搞出了个ServiceMap,并把原来的这张ServiceRecord表格搬到ServiceMap里了。我们可以画一张示意图,来说明Android代码的变迁:
(Android 2.3版示意图)
(Android 5.1版示意图)
但是,不管ServiceRecord表格被放到哪里,其本质都是一致的。而AMS必须保证在实际启动一个Service之前查到或创建对应的ServiceRecord节点。
在ServiceRecord类中有不少成员变量,其中有一个app域,专门用来记录Service对应的用户进程的ProcessRecord。当然,一开始这个app域是为null的,也就是说ServiceRecord节点还没有和用户进程关联起来,待Service真正启动之后,ServiceRecord的app域就有实际的值了。
现在我们继续看startServiceLocked()函数,它在找到ServiceRecord节点之后,开始调用startServiceInnerLocked()。我们可以绘制一下相关的调用关系:
请注意上图中bringUpServiceLocked()里的内容。此时大体上分三种情况:
1)如果ServiceRecord已经和Service寄身的用户进程关联起来了,此时ServiceRecord的app域以及app.thread域都是有值的,那么只调用 sendServiceArgsLocked()即可。这一步会让Service间接走到大家熟悉的onStartCommand()。
2)如果尚未启动Service寄身的用户进程,那么需要调用mAm.startProcessLocked()启动用户进程。
3)如果Service寄身的用户进程已经启动,但尚未和ServiceRecord关联起来,那么调用realStartServiceLocked(r, app, execInFg);这种情况下,会让Service先走到onCreate(),而后再走到onStartCommand()。
我们重点看第三种情况,realStartServiceLocked()的调用示意图如下:
看到了吧,无非是利用scheduleXXX()这样的函数,来通知用户进程去做什么事。以后大家看到这种以schedule打头的函数,可以直接打开ActivityThread.java文件去查找其对应的实现函数。比如scheduleCreateService()的代码如下:
【frameworks/base/core/java/android/app/ActivityThread.java】
public final void scheduleCreateService(IBinder token,
ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
updateProcessState(processState, false);
CreateServiceData s = new CreateServiceData();
s.token = token;
s.info = info;
s.compatInfo = compatInfo;
sendMessage(H.CREATE_SERVICE, s);
}
它发出的CREATE_SERVICE消息,会由ActivityThread的内嵌类H处理,H继承于Handler,而且是在UI主线程里处理消息的。可以看到,处理CREATE_SERVICE消息的函数是handleCreateService():
case CREATE_SERVICE:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceCreate");
handleCreateService((CreateServiceData)msg.obj);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
另外,请大家注意realStartServiceLocked()传给scheduleCreateService()函数的第一个参数就是ServiceRecord类型的r,而ServiceRecord本身是个Binder实体噢。待“传到”应用进程时,这个Binder实体对应的Binder代理被称作token,记在了CreateServiceData对象的token域中。现在CreateServiceData对象又经由msg.obj传递到消息处理函数里,并进一步作为参数传递给handleCreateService()函数。
handleCreateService()的代码如下:
private void handleCreateService(CreateServiceData data) {
. . . . . .
LoadedApk packageInfo = getPackageInfoNoCheck(
data.info.applicationInfo, data.compatInfo);
Service service = null;
. . . . . .
java.lang.ClassLoader cl = packageInfo.getClassLoader();
service = (Service) cl.loadClass(data.info.name).newInstance();
. . . . . .
ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
context.setOuterContext(service);
Application app = packageInfo.makeApplication(false, mInstrumentation);
// 注意,ServiceRecord实体对应的代理端,就是此处的data.token。
service.attach(context, this, data.info.name, data.token, app,
ActivityManagerNative.getDefault());
service.onCreate();
mServices.put(data.token, service);
. . . . . .
}
简单地说就是,先利用反射机制创建出Service对象:
service = (Service) cl.loadClass(data.info.name).newInstance();
然后再调用该对象的onCreate()成员函数:
service.onCreate();
而因为handleCreateService()函数本身是在用户进程的UI主线程里执行的,所以service的onCreate()函数也就是在UI主线程里执行的。常常会有人告诫新手,不要在onCreate()、onStart()里执行耗时的操作,现在大家知道是为什么了吧,因为在UI主线程里执行耗时的操作不但会引起界面卡顿,严重的还会导致ANR报错。
另外,在执行到上面的service.attach()时,那个和ServiceRecord对应的代理端token也传进来了:
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager) {
attachBaseContext(context);
mThread = thread;
mClassName = className;
mToken = token; // 注意这个token噢,它的对端就是AMS里的ServiceRecord!
mApplication = application;
mActivityManager = (IActivityManager)activityManager;
mStartCompatibility = getApplicationInfo().targetSdkVersion
< Build.VERSION_CODES.ECLAIR;
}
可以看到,token记录到ServiceRecord的mToken域了。
最后,新创建出的Service对象还会记录进ActivityThread的mServices表格去,于是我们可以在前文示意图的基础上,再画一张新图:
这就是startService()启动服务的大体过程。上图并没有绘制“调用startService()的用户进程”,因为当Service启动后,它基本上就和发起方没什么关系了。
4.再说bindService()
接下来我们来说说bindService(),它可比startService()要麻烦一些。当一个用户进程bindService()时,它需要先准备好一个实现了ServiceConnection接口的对象。ServiceConnection的定义如下:
public interface ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service);
public void onServiceDisconnected(ComponentName name);
}
在Android平台上,每当用户调用bindService(),Android都将之视作是要建立一个新的“逻辑连接”。而当连接建立起来时,系统会回调ServiceConnection接口的onServiceConnected()。另一方面,那个onServiceDisconnected()函数却不是在unbindService()时发生的。一般来说,当目标service所在的进程意外挂掉或者被杀掉时,系统才会回调onServiceDisconnected(),而且,此时并不会销毁之前的逻辑连接,也就是说,那个“逻辑连接”仍然处于激活状态,一旦service后续再次运行,系统会再次回调onServiceConnected()。
在Android平台上,要和其他进程建立逻辑连接往往都需要利用binder机制。那么,发起bindService()的用户进程又是在哪里创建逻辑连接需要的binder实体呢?我们可以看看bindServiceCommon()函数的代码截选:
【frameworks/base/core/java/android/app/ContextImpl.java】
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,
UserHandle user) {
IServiceConnection sd;
. . . . . .
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
mMainThread.getHandler(), flags); // 请注意返回的sd!
. . . . . .
IBinder token = getActivityToken();
. . . . . .
int res = ActivityManagerNative.getDefault().bindService(
mMainThread.getApplicationThread(), getActivityToken(),
service, service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, user.getIdentifier());
. . . . . .
}
请大家注意mPackageInfo.getServiceDispatcher()那一句,它返回的就是binder实体。此处的mPackageInfo是用户进程里和apk对应的LoadedApk对象。getServiceDispatcher()的代码如下:
【frameworks/base/core/java/android/app/LoadedApk.java】
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) {
sd = map.get(c);
}
if (sd == null) {
sd = new ServiceDispatcher(c, context, handler, flags);
if (map == null) {
map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
mServices.put(context, map);
}
map.put(c, sd);
} else {
sd.validate(context, handler);
}
return sd.getIServiceConnection(); // 注意,返回ServiceDispatcher内的binder实体
}
}
也就是说,先尝试在LoadedApk的mServices表中查询ServiceDispatcher对象,如果查不到,就重新创建一个。ServiceDispatcher对象会记录下从用户处传来的ServiceConnection引用。而且,ServiceDispatcher对象内部还含有一个binder实体,现在我们可以通过调用sd.getIServiceConnection()一句,返回这个内部的binder实体。
现在我们画一张示意图,来说明发起bindService()动作的用户进程中的样子:
LoadedApk中的mServices是常见的表中表形式,定义如下:
private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
这里比较有趣的是,第一层表的key类型为Context,大家不妨想一想,如果我们的应用里有两个activity都去绑定同一个Service会怎么样?很明显,在mServices表里就会有两个不同的子表,也会创建出两个不同的ServiceDispatcher对象,而且即便我们在调用bindService()时传入同一个ServiceConnection对象,依旧会有两个ServiceDispatcher对象。示意图如下:
说完了发起bindService()动作的用户进程一侧,接下来我们再来看AMS一侧的代码。对应的bindService()代码如下:
【frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java】
public int bindService(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags, int userId) {
enforceNotIsolatedCaller("bindService");
// Refuse possible leaked file descriptors
if (service != null && service.hasFileDescriptors() == true) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
synchronized(this) {
return mServices.bindServiceLocked(caller, token, service, resolvedType,
connection, flags, userId);
}
}
主要工作集中在mServices.bindServiceLocked()一句。请注意bindService()的倒数第三个参数,它对应的就是上图中ServiceDispatcher的mIServiceConnection域记录的binder实体。至于bindService()的第二个参数IBinder token,其实记录的是发起绑定动作的Activity的token,当然,如果发起者是其他非Activity型的对象,那么这个参数应该是null。
上面的代码最终走到mServices.bindServiceLocked()一句。我们摘录一下bindServiceLocked()的代码:
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】
int bindServiceLocked(IApplicationThread caller, IBinder token,
Intent service, String resolvedType,
IServiceConnection connection, int flags, int userId) {
. . . . . .
final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);
. . . . . .
ActivityRecord activity = null;
. . . . . .
activity = ActivityRecord.isInStackLocked(token);
. . . . . .
ServiceLookupResult res = retrieveServiceLocked(service, resolvedType,
Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg);
. . . . . .
ServiceRecord s = res.record;
. . . . . .
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); // 注意这个b!
ConnectionRecord c = new ConnectionRecord(b, activity,
connection, flags, clientLabel, clientIntent);
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
. . . . . .
clist.add(c);
b.connections.add(c);
. . . . . .
b.client.connections.add(c);
. . . . . .
clist = mServiceConnections.get(binder);
. . . . . .
clist.add(c);
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
. . . . . .
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
return 0;
}
}
. . . . . .
if (s.app != null && b.intent.received) {
. . . . . .
c.conn.connected(s.name, b.intent.binder); // 注意这个!
. . . . . .
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
. . . . . .
}
尽管这个函数里有不少技术细节,而且涉及到多个映射表,但它的总体意思大概是这样的。每当用户调用bindService()时,Android都将之看作是要建立一个新的“逻辑连接”,而每个逻辑连接都对应一个ConnectionRecord节点,所以最终的表现肯定是向ServiceRecord内部的某张映射表里添加一个新的ConnectionRecord节点。当然,在实际运作时,这个节点还会记录进其他几个映射表里(比如系统总映射表),但这不影响我们理解问题。
那么为什么系统里会有那么多映射表呢?这大概是为了在复杂的网状联系中快速便捷地找到相关的节点。我们知道,一个用户进程可以绑定多个Service,而一个Service也可以被多个用户进程绑定,这就是网状结构。示意图如下:
前文我们已经说过,绑定时使用的ServiceConnection对象在发起端其实对应了一个ServiceDispatcher对象,现在,ServiceDispatcher的mIServiceConnection传递给bindServiceLocked(),也就是那个IServiceConnection connection参数。如果系统要建立“逻辑连接”,那么就需要把IServiceConnection代理端记录到ConnectionRecord节点里。
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags,
clientLabel, clientIntent);
另外,请大家注意bindServiceLocked()里那个重要的AppBindRecord节点。
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp); // 注意这个b!
相传对于一个Service而言,有多少应用和它建立了绑定关系,就会有多少个AppBindRecord节点,要不怎么叫App-Bind呢?当然,一个应用里可以有多个地方发起绑定动作,所以AppBindRecord里需要用一个ArraySet<ConnectionRecord>记录下每个绑定动作对应的逻辑连接节点。
有了这些认识后,我们可以画一张“发起端用户进程”和“系统进程”之间的示意图:
在ConnectionRecord被记录进合适的表后,要开始和目标service建立连接了。我们可以看到,bindServiceLocked()会尝试呼叫起目标service。
if ((flags&Context.BIND_AUTO_CREATE) != 0) {
. . . . . .
if (bringUpServiceLocked(s, service.getFlags(), callerFg, false) != null) {
return 0;
}
}
看到这句if语句,大家应该知道调用bindService()时为什么常常要加上BIND_AUTO_CREATE了吧:
bindService(intent, conn, Context.BIND_AUTO_CREATE);
假设目标Service之前已经启动过,那么现在的绑定流程会走到if (s.app != null && b.intent.received)分支里,于是调用到c.conn.connected()。
if (s.app != null && b.intent.received) {
. . . . . .
c.conn.connected(s.name, b.intent.binder); // 注意这个!
. . . . . .
if (b.intent.apps.size() == 1 && b.intent.doRebind) {
requestServiceBindingLocked(s, b.intent, callerFg, true);
}
} else if (!b.intent.requested) {
requestServiceBindingLocked(s, b.intent, callerFg, false);
}
但是,如果Service之前并未启动,而且这次是我们首次绑定service,那么不就到不了c.conn.connected()了吗?此时应该会走到else if 分支,调用到requestServiceBindingLocked(),该函数主要是向目标service发起绑定的请求,但是现在连service寄身的进程可能都还没有启动,request又有什么意义呢?所以,此处调用requestServiceBindingLocked()也许不会有什么重大意义。这并不是说requestServiceBindingLocked()不重要,而是说它真正起作用的地方也许不在这里。为了说明问题,我们得看一下刚刚调用的bringUpServiceLocked()函数:
private final String bringUpServiceLocked(ServiceRecord r,
int intentFlags, boolean execInFg, boolean whileRestarting) {
. . . . . .
ProcessRecord app;
. . . . . .
if (app == null) {
if ((app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
"service", r.name, false, isolated, false)) == null) {
. . . . . .
bringDownServiceLocked(r);
return msg;
}
. . . . . .
}
// 注意此处的mPendingServices!
if (!mPendingServices.contains(r)) {
mPendingServices.add(r);
}
. . . . . .
}
bringUpServiceLocked()通过调用mAm.startProcessLocked()启动目标service寄身的进程,而后会把ServiceRecord节点记入mPendingServices数组列表中。
待后续service寄身的进程成功启动后,会辗转调用到attachApplicationLocked(),该函数的代码截选如下:
boolean attachApplicationLocked(ProcessRecord proc, String processName)
throws RemoteException {
. . . . . .
if (mPendingServices.size() > 0) {
ServiceRecord sr = null;
. . . . . .
for (int i=0; i<mPendingServices.size(); i++) {
sr = mPendingServices.get(i);
. . . . . .
mPendingServices.remove(i);
i--;
proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,
mAm.mProcessStats);
realStartServiceLocked(sr, proc, sr.createdFromFg);
. . . . . .
}
. . . . . .
}
也就是说,当目标service寄身的进程启动后,会从mPendingServices数组列表中把ServiceRecord节点删除,并进一步调用realStartServiceLocked():
private final void realStartServiceLocked(ServiceRecord r,
ProcessRecord app, boolean execInFg) throws RemoteException {
. . . . . .
r.app = app;
. . . . . .
app.services.add(r);
. . . . . .
app.thread.scheduleCreateService(r, r.serviceInfo,
mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
app.repProcState);
. . . . . .
requestServiceBindingsLocked(r, execInFg);
. . . . . .
sendServiceArgsLocked(r, execInFg, true);
. . . . . .
}
此处调用的app.thread.scheduleCreateService()会间接导致目标service走到大家熟悉的onCreate()。而后还会调用requestServiceBindingsLocked()。这里大概才是requestServiceBindingLocked()真正起作用的地方。
requestServiceBindingsLocked()的代码如下:
private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg) {
for (int i=r.bindings.size()-1; i>=0; i--) {
IntentBindRecord ibr = r.bindings.valueAt(i);
if (!requestServiceBindingLocked(r, ibr, execInFg, false)) {
break;
}
}
}
private final boolean requestServiceBindingLocked(ServiceRecord r,
IntentBindRecord i, boolean execInFg, boolean rebind) {
. . . . . .
r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
r.app.repProcState);
if (!rebind) {
i.requested = true;
}
i.hasBound = true;
i.doRebind = false;
. . . . . .
}
终于看到调用scheduleBindService()了。
到了“Service所属的进程”里,scheduleBindService()执行的代码如下:
public final void scheduleBindService(IBinder token, Intent intent,
boolean rebind, int processState) {
updateProcessState(processState, false);
BindServiceData s = new BindServiceData();
s.token = token; // 对应AMS里的ServiceRecord
s.intent = intent;
s.rebind = rebind;
......
sendMessage(H.BIND_SERVICE, s);
}
所发出的BIND_SERVICE消息,会导致service寄身的进程走到handleBindService():
【frameworks/base/core/java/android/app/ActivityThread.java】
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
. . . . . .
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
}
. . . . . .
}
回调了目标service的onBind(),而后向AMS发布自己,即调用publishService()。
publishService()的第一个参数指代AMS里的ServiceRecord节点,而最后一个参数是目标Service的onBind()函数返回的服务对象。因此publish函数其实起的是衔接的作用。
在AMS一侧,publish动作最终会走到publishServiceLocked():
【frameworks/base/services/core/java/com/android/server/am/ActiveServices.java】
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
. . . . . .
Intent.FilterComparison filter
= new Intent.FilterComparison(intent);
IntentBindRecord b = r.bindings.get(filter);
if (b != null && !b.received) {
b.binder = service; // 记录下目标进程对应的binder代理
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);
. . . . . .
c.conn.connected(r.name, service); // 通告bindService发起方
. . . . . .
}
}
}
. . . . . .
}
这里又牵扯到ServiceRecord的connections映射表,在介绍bindServiceLocked()函数时,我们其实已经看到过几句相关的代码了,现在再列举一下:
IBinder binder = connection.asBinder();
ArrayList<ConnectionRecord> clist = s.connections.get(binder);
. . . . . .
clist.add(c);
也就是说,我们在绑定服务时创建的那个ConnectionRecord节点,会同时将该节点记录进ServiceRecord的connections映射表,而映射表的key值就是逻辑上指向发起端的IServiceConnection代理。
为什么要有这个映射表呢?很简单,就是为了快速地找出所有使用相同IServiceConnection.Stub对象,完成绑定动作的ConnectionRecord节点。请大家设想,我们完全可以在一个Activity里,使用同一个ServiceConnection对象发起多次绑定同一个service的动作,发起绑定时使用的intent也许会不同,但最终有可能绑定到同一个service,此时,上面代码中s.connections.get(binder)得到的ArrayList就会含有多个元素啦。
好,介绍完connections映射表,我们继续看publishServiceLocked()里的那句c.conn.connected()。ConnectionRecord节点的conn成员也是IServiceConnection类型的代理端,所以这一句调用最终是远程调用到发起端的ServiceDispatcher里的mIServiceConnection对象,该对象是InnerConnection类型的。
InnerConnection的connected()函数如下:
【frameworks/base/core/java/android/app/LoadedApk.java】
private static class InnerConnection extends IServiceConnection.Stub {
final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
. . . . . .
public void connected(ComponentName name, IBinder service) throws RemoteException {
LoadedApk.ServiceDispatcher sd = mDispatcher.get();
if (sd != null) {
sd.connected(name, service);
}
}
}
而ServiceDispatcher的connected()函数如下:
public void connected(ComponentName name, IBinder service) {
if (mActivityThread != null) {
mActivityThread.post(new RunConnection(name, service, 0));
} else {
doConnected(name, service);
}
}
private final class RunConnection implements Runnable {
. . . . . .
public void run() {
if (mCommand == 0) {
doConnected(mName, mService);
} else if (mCommand == 1) {
doDeath(mName, mService);
}
}
. . . . . .
}
【frameworks/base/core/java/android/app/LoadedApk.java】
public void doConnected(ComponentName name, IBinder service) {
. . . . . .
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) {
. . . . . .
}
. . . . . .
// If there is a new service, it is now connected.
if (service != null) {
mConnection.onServiceConnected(name, service); // 终于看到onServiceConnected了!
}
}
至此,我们终于看到大家熟悉的onServiceConnected()回调啦!而传来的service参数,就是我们希望绑定的那个service提供的binder代理。现在我们可以说已经打通了bindService()动作涉及的三方关系:发起方、AMS、目标Service。我们不妨再画一张图看看:
有关Service的基本机制和启动流程,我们就先说这么多吧。以后我们再补充其他方面的内容。
以上是关于Android Service演义的主要内容,如果未能解决你的问题,请参考以下文章
java [Intent] Intent片段以启动Activity,Service或发送广播。 #android_snippet #android
What's the difference between @Component, @Repository & @Service annotations in Spring?(代码片段
Android 从 Service 到 Client 的通信
Nginx——Nginx启动报错Job for nginx.service failed because the control process exited with error code(代码片段