Android Service演义

观书散遗帙,探古穷至妙。这篇文章主要讲述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,其继承关系如下图所示:
Android Service演义

文章图片

看了这张图,请大家务必理解,Service只是个“ 上下文” (Context)对象而已,它和进程、线程是没什么关系的。
在AMS中,负责管理service的ServiceRecord节点本身就是个binder实体。当AMS向应用进程发出语义,要求其创建service对象时,会把ServiceRecord通过binder机制“ 传递” 给应用进程。这样,应用进程的ActivityThread在处理AMS发来的语义时,就可以得到一个合法的binder代理,这个binder代理最终会被记录在Service对象中,如此一来,Service实体就和系统里的ServiceRecord关联起来了。我们画个图来说明,假如一个应用进程里启动了两个不同的Service,那么当service创建成功之后,AMS和用户进程之间就会形成如下关系示意图:
Android Service演义

文章图片

当然,如果用户进程和service再多一点儿也完全没问题,此时会形成下图:
Android 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 Service演义

文章图片

(Android 2.3版示意图)
Android Service演义

文章图片

(Android 5.1版示意图)
但是,不管ServiceRecord表格被放到哪里,其本质都是一致的。而AMS必须保证在实际启动一个Service之前查到或创建对应的ServiceRecord节点。
在ServiceRecord类中有不少成员变量,其中有一个app域,专门用来记录Service对应的用户进程的ProcessRecord。当然,一开始这个app域是为null的,也就是说ServiceRecord节点还没有和用户进程关联起来,待Service真正启动之后,ServiceRecord的app域就有实际的值了。
现在我们继续看startServiceLocked()函数,它在找到ServiceRecord节点之后,开始调用startServiceInnerLocked()。我们可以绘制一下相关的调用关系:
Android Service演义

文章图片

请注意上图中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()的调用示意图如下:
Android Service演义

文章图片

看到了吧,无非是利用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表格去,于是我们可以在前文示意图的基础上,再画一张新图:
Android Service演义

文章图片

这就是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()动作的用户进程中的样子:
Android Service演义

文章图片

LoadedApk中的mServices是常见的表中表形式,定义如下:
private final ArrayMap< Context, ArrayMap< ServiceConnection,  LoadedApk.ServiceDispatcher> > mServices

这里比较有趣的是,第一层表的key类型为Context,大家不妨想一想,如果我们的应用里有两个activity都去绑定同一个Service会怎么样?很明显,在mServices表里就会有两个不同的子表,也会创建出两个不同的ServiceDispatcher对象,而且即便我们在调用bindService()时传入同一个ServiceConnection对象,依旧会有两个ServiceDispatcher对象。示意图如下:
Android Service演义

文章图片

说完了发起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也可以被多个用户进程绑定,这就是网状结构。示意图如下:
Android 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> 记录下每个绑定动作对应的逻辑连接节点。
有了这些认识后,我们可以画一张“ 发起端用户进程” 和“ 系统进程” 之间的示意图:
Android Service演义

文章图片


在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。我们不妨再画一张图看看:
Android Service演义

文章图片

【Android Service演义】有关Service的基本机制和启动流程,我们就先说这么多吧。以后我们再补充其他方面的内容。
 
  转自https://my.oschina.net/youranhongcha/blog/710046

    推荐阅读