Android Binder框架实现目录:
Android Binder框架实现之Binder的设计思想 Android Binder框架实现之何为匿名/实名Binder Android Binder框架实现之Binder中的数据结构 Android Binder框架实现之Binder相关的接口和类 Android Binder框架实现之Parcel详解之基本数据的读写 Android Binder框架实现之Parcel read/writeStrongBinder实现 Android Binder框架实现之servicemanager守护进程 Android Binder框架实现之defaultServiceManager()的实现 Android Binder框架实现之Native层addService详解之请求的发送 Android Binder框架实现之Native层addService详解之请求的处理 Android Binder框架实现之Native层addService详解之请求的反馈 Android Binder框架实现之Binder服务的消息循环 Android Binder框架实现之Native层getService详解之请求的发送 Android Binder框架实现之Native层getService详解之请求的处理 Android Binder框架实现之Native层getService详解之请求的反馈 Android Binder框架实现之Binder Native Service的Java调用流程 Android Binder框架实现之Java层Binder整体框架设计 Android Binder框架实现之Framework层Binder服务注册过程源码分析 Android Binder框架实现之Java层Binder服务跨进程调用源码分析 Android Binder框架实现之Java层获取Binder服务源码分析 Android Binder框架实现之bindService详解
本篇博客编写思路总结和关键点说明: 为了更加方便的读者阅读博客,通过导读思维图的形式将本博客的关键点列举出来,从而方便读者取舍和阅读!
小伙们还记得前面博客Android Binder框架实现之何为匿名/实名Binder中我们分析匿名/实名Binder的时候重点以bindService为例说明了匿名Binder在该调用过程中的运用(上篇的重点主要是匿名Binder在传输中如何在驱动中完成匿名Binder的实体节点的生成和引用的创建),那么在今天的博客中我们重点以bindService为例来分析匿名Binder的实际运用过程。 当然小伙们也可以抛开上面的匿名/实名Binder等相关知识点,单纯当成是bindService的源码分析也不为过,这个就看小伙们的出发点了,是分析匿名Binder的应用还是bindService的流程就看小伙们各取所需了。
注意:本篇的介绍是基于Android 7.xx平台为基础的,其中涉及的代码路径如下: frameworks/base/services/core/java/com/android/server/am/ --- ActivityManagerService.java --- ActiveServices.java --- ServiceRecord.java --- ProcessRecord.java frameworks/base/core/java/android/app/ --- IActivityManager.java --- ActivityManagerNative.java (内部包含AMP) --- ActivityManager.java --- IApplicationThread.java --- ApplicationThreadNative.java (内部包含ATP) --- ActivityThread.java (内含ApplicationThread) --- ContextImpl.java frameworks/base/core/java/android/app/LoadedApk.java frameworks/base/core/java/android/app/IServiceConnection.aidl frameworks/base/core/java/android/content/ServiceConnection.java frameworks/base/core/java/android/util/Singleton.java 并且在后续的源码分析过程中为了简述方便,会将做如下简述: ApplicationThreadProxy简称为ATP ActivityManagerProxy简称为AMP ActivityManagerService简称为AMS ActivityManagerNative简称AMN ApplicationThreadNative简称ATN PackageManagerService简称为PKMS ApplicationThread简称为AT ActivityServices简称为AS在正式开始着手bindService这一篇章的撰写之前,我一直在思考一个问题就是要以什么角度入手才能将本篇博客讲得更加的入味,对于我最简单而言就是平铺直述以源码的脉络为基础跟着代码分析直接开撸,转念一想这个虽然是最简单粗暴的套路,但是估计读者不会买账,因为跟读源码是最枯燥和乏味的。那么我们就跳出源码,站在上帝的视角出发,以进程交互的角度出发来分析bindService,我想这样反而小伙们更加容易接受! 在bindService的源码中将要涉及到三个主要的进程,分别是bindService发起端进程,system_server进程和目的端Service进程, 我们将会以这三个进程角度出来阐述bindService是怎么在这三个进程之间辗转腾挪达到远程绑定服务或者说是Binder传递功能的。对于绝大部分小伙们来说遇到一个新的知识点,一定是先从实例入手先使用,然后再深入原理及其实现,我们这里也不例外,先让我们看看bindService的实例怎么使用!
bindService的实例涉及到目的端Service进程和client端发起端进程,让我们来开写,好久没有写过实例了让我们入手一番!
bindService能实现的一个前提就是Android为我们提供了一套AIDL(Android Interface Definition Language)即Android接口定义语言,来帮助我们实现远程Binder的通信(当然我们也可以不使用AIDL语言,直接硬编码实现也行可以参见博客Android Binder实战开发指南之不借助AIDL创建Java层Binder服务)。好了前面说了这么多了,直接不如正题,编码一个aidl文件如下:
interface IAidlFun { void doAction(); }此时编译器会对aidl文件自动编译生成,IAidlFun.java文件,我们看看其神秘面纱,我想如何小伙们对Binder有过一定的基础,对其中的代码就不会陌生了,这就是Android为了我们快速能使用Binder而做的一个模板啊,即生成通信层代码模板,而用户完全只用关心业务层的逻辑,降低开发难度。
/* * This file is auto-generated. DO NOT MODIFY. * Original file: E:\\workspace\\PaxForAll\\src\\com\\example\\api\\aidl\\IAidlFun.aidl */ package com.example.api.aidl; public interface IAidlFun extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.api.aidl.IAidlFun { private static final java.lang.String DESCRIPTOR = "com.example.api.aidl.IAidlFun"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.api.aidl.IAidlFun * interface, generating a proxy if needed. */ public static com.example.api.aidl.IAidlFun asInterface( android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.api.aidl.IAidlFun))) { return ((com.example.api.aidl.IAidlFun) iin); } return new com.example.api.aidl.IAidlFun.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_doAction: { data.enforceInterface(DESCRIPTOR); this.doAction(); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.api.aidl.IAidlFun { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public void doAction() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_doAction, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); _data.recycle(); } } } static final int TRANSACTION_doAction = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public void doAction() throws android.os.RemoteException; }bindService感觉用起来好爽,好简单啊!可是其中涉及的过程调用和源码逻辑各位小伙们都清楚吗,不清楚也没有关系,让我们来一起搞清楚!
还是老套路,在正式开始分析之前我们还是磨刀不误砍柴工,我们还是对即将要分析的源码先来梳理梳理要准备的知识点,这样后续分析起来也会简单顺手一些。
bindService调用过程中会牵涉到目的端进程的创建(即目的端服务进程没有创建的前提下),这个虽然不是本文的重点,但是还是大概看下其示意图,后面也会大概过下:
为了不耽误小伙们的时间,如果看了下面的时序图觉得有信心跟下去深入的可以接着往下分析,如果觉得没有毅力深入的可以就此打住了(因为分析源码真的是一件枯燥乏味的事情)!
在正式开始bindService的分析之前,将要涉及到涉及到Context的继承关系类图,从下面的类图中可以看出,Context是一个接口(提供了很多的接口方法),ContextImp和ContextWrapper都是其实现类,我们常用的Activity、Service、Application都直接或间接继承自ContextWrapper。
这里我们可以看到bindService会调用到ContextWrapper类的bindService中,源码如下:
@Override //ContextWrapper.java public boolean bindService(Intent service, ServiceConnection conn, int flags) { return mBase.bindService(service, conn, flags);//这里的mBase指向ContextImpl(怎么来的这里就不分析了,给出结论),其方法bindService见章节2.2 }这里我们有如下几点需要注意:
bindService方法第二个入参ServiceConnection为一个接口类型对象ServiceConnection内部方法onServiceConnected的一个入参参数为IBinder数据类型,它就是我们最终获取的远程Binder实体的服务代理端(同时它也是一个匿名Binder)。通过我们前面的博客知道IBinder类型是可以跨进程传递的,而这里也运用了IBinder的这个特性从而实现了bindService。 public interface ServiceConnection { //这里的入参service就是我们最终要获取的远程Binder Service的远程代理端 public void onServiceConnected(ComponentName name, IBinder service); public void onServiceDisconnected(ComponentName name); }无需多言,直接调用方法bindServiceCommon进行下一步的处理。
这里的IServiceConnection是一个aidl生成的Binder通讯工具类文件,其定义如下所示(可以看到它被定义为oneway类型的,即是一个非阻塞的Binder调用):
//IServiceConnection.aidl oneway interface IServiceConnection { //注意这里的入参IBinder service和ServiceConnection的方法中入参IBinder service,都是相同的入参那么有啥关联,这里先预留一个疑点,到了后续小伙伴们应该就清楚了 void connected(in ComponentName name, IBinder service); }我们回过头来看看bindServiceCommon的主要功能,然后对其逐个出击,一一攻破:
创建对象内部静态类LoadedApk.ServiceDispatcher.InnerConnection的对象调用AMS的代理端AMP,向AMS服务发起bindService请求其中bindService的入参mMainThread.getApplicationThread()方法返回的是ApplicationThread对象, 该对象继承于ApplicationThreadNative(Binder服务端),这个ApplicationThread对象很重要,因为正是通过它串联其了AMS对发起端进程的ActivityThread的交互(如果把ApplicationThread当作服务端,那么此时AMS相关于ApplicationThread而言就是客户端)。其两者之间的关系建立详见下述的示意图,即AMS持有ApplicationThread的代理端,而应用端进程持有AMS的代理端AMP,二者相互持有各自Binder服务端的代理端进而完成了二者之间的RPC调用。
在正式开始介绍getServiceDispatcher获取内部静态类LoadedApk.ServiceDispatcher.InnerConnection的对象之前,我们先看看LoadedApk的类图关系,如下(先熟悉熟悉,这样后续分析源码不会迷失):
//LoadedApk.java private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices = new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>(); public final IServiceConnection getServiceDispatcher(ServiceConnection c, Context context, Handler handler, int flags) { synchronized (mServices) { LoadedApk.ServiceDispatcher sd = null;//ServiceDispatcher内部类 ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);//查找mService中是否存在context的key if (map != null) {//如果存在这样的map,则继续查找map中是否存在ServiceConnection类型的实例c的key sd = map.get(c);// } if (sd == null) {//没有则创建 //创建服务分发对象,注意这里的参数c是ServiceConnection对象,详见2.3.2 sd = new ServiceDispatcher(c, context, handler, flags); if (map == null) { map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>(); mServices.put(context, map); } //以ServiceConnection为key,ServiceDispatcher为value保存到map中 map.put(c, sd); } else { sd.validate(context, handler); } //返回内部类对象InnerConnection实例 return sd.getIServiceConnection(); } }在正式开始上述的源码分析前,我们先介绍介绍其中涉及的一个重要的数据结构对象mServices:
可以看到它是一个Hashmap类型的数据,其key为Context类型对象然后mServices的value也是一个Hasmap,存放着Context对象所对应的ServiceConnection以及所对应的LoadedApk.ServiceDispatcher对象,即一个Context对象可以对应很多个ServiceConnection,但是同一个只会被创建一次路障扫清了,让我们接着分析该方法,可以该方法返回LoadedApk.ServiceDispatcher.InnerConnection类型对象,该对象继承于IServiceConnection.Stub, 该类是由IServiceConnection.aidl类型文件由编译器怎懂自动生成的作为Binder服务端的实体端类。其中IServiceConnection.aidl在2.2章节已经有给出过了,就不放上了。
通过源码我们发现ServiceDispatcher是LoadedApk的内部类。InnerConnection是ServiceDispatcher的静态内部类,是不是有点拗口啊,这就是为什么我在前面先其奉上了LoadedApk的类图的原因。其中ServiceDispatcher有一个重要的方法getIServiceConnection(),通过该方法返回的便是在其构造方法中创建的InnerConnection对象。而这个InnerConnection也是bindService中的关键类,为啥这么说呢,往后分析小伙们就知道了!
继续回到章节2.3在获取到InnerConnection实例对象sd之后,调AMN.getDefault().bindService,这里牵涉到一个重要的方法AMN.getDefault(),其实它在我们的博客中 Android Binder框架实现之Java层获取Binder服务源码分析已经有详细的介绍和分析了,但是为了博客的连贯性还是简单过下(主要是为了不太熟悉的小伙伴们)。
//ActivityManagerNative.java static public IActivityManager getDefault() { return gDefault.get(); }这里的gDefault是Singleton对象实例,而Singleton我们可以看到是一个模板类对象,并且提供了一个单例方法,其定义如下:
//Singleton.java public abstract class Singleton<T> { private T mInstance; protected abstract T create(); public final T get() { synchronized (this) { if (mInstance == null) {//采用单例模式 mInstance = create(); } return mInstance; } } }我们将IActivityManager带入Singleton,得到如下的create方法过程:
//ActivityManagerNative.java private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() { protected IActivityManager create() { //此处等价于IBinder b = new BinderProxy(new BpBinder(handle)); IBinder b = ServiceManager.getService("activity"); if (false) { Log.v("ActivityManager", "default service binder = " + b); } //此处等价于IActivityManager am = new ActivityManagerProxy(new BinderProxy(new BpBinder(handle))) IActivityManager am = asInterface(b); if (false) { Log.v("ActivityManager", "default service = " + am); } return am; } }; //注意此处我们的入参是BinderProxy类型,所以会走代理端 static public IActivityManager asInterface(IBinder obj) { if (obj == null) { return null; } IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor); if (in != null) { return in; } //即会走到此处 return new ActivityManagerProxy(obj); }这里即最终经过层层转换得到了AMS服务的代理端ActivityManagerProxy,进而借助它完成对AMS服务的RPC请求。
AMN.getDefault()的调用流程基本分析结束了,我们对其小结一下:
AMN.getDefault()最终获取了AMS的远程Binder代理端AMPAMS的Binder通信过程中提供了一个IActivityManager服务业务层接口,AMP类与AMS类都实现了IActivityManager接口方法,区别不同给的是AMS端显示了真正的具体服务,而AMP端是封装了相关的通信传输逻辑。AMP作为Binder通信的服务代理端,而AMS作为Binder通信的服务端实体,根据Android Binder框架实现之Java层Binder服务跨进程调用源码分析,AMP.bindService()最终调用AMS.bindService(),整个流程图如下:这里如果对Binder框架熟悉的小伙们应该对上述的调用过程是见怪不怪了,但是有几个点我们需要注意:
bindService中调用了三次Parcel类的方法writeStrongBinder(),这里我们需要注意writeStrongBinder()这三次写入的是Binder实体代理端还是代理端,是实名Binder还是匿名Binder。这里的mRemote指向BinderProxy,而BinderProxy持有C++端的BpBinder,而BpBinder作为远程Binder实体的通信代理端,最后借助Binder驱动和AMS通信,最后调用到ActivityManagerNative的onTransact()方法中通过上面的层层冲关,打怪我们跳出了发起端进程,来到了system_server进程,让我们接着分析看看system_server是怎么处理bindService的RPC请求的。
在正式开始上述的源码分析前,我们先来阐述一个重要的知识点,即在这个调用过程中涉及到两个进程,不妨令bindService的发起进程记为进程Process_A,AMS Service所属进程记为进程Process_B;那么进程Process_A通过Binder机制(采用IActivityManager接口)向进程Process_B发起请求服务,进程Process_B则通过Binder机制(采用IApplicationThread接口)向进程Process_A发起请求服务。也就是说进程Process_A与进程Process_B能相互间主动发起请求,进而完成进程通信,但是这里有一点需要注意IApplicationThread的Binder实体端并没有注册到servicemanager进程中,它是一个依赖于实名Binder的匿名Binder。 这里涉及IApplicationThread很重要,它串联起了AMS对App进程的生命周期及其其它的控制,那么下面直接把其相关的类图展示如下:
这里的IApplicationThread与IActivityManager的Binder通信原理一样,ATP作为Binder通信的客户端,ATN作为Binder通信的服务端,其中ApplicationThread继承ATN类,覆写其中的部分方法。
接着继续分析onTransact方法,其根据AMP传递过来的code值进入BIND_SERVICE_TRANSACTION分支,然后解读取通过Binder驱动传递过来的数据,解析完成之后调用AMN的方法bindService继续未完成之工作(这里的bindService在AMS服务中具体实现),这里从驱动中获取到数据然后解析这里就不重点关注了,我们这里重点关注一下AMN.bindService的几个入参:
参数app: 发起端进程通过Binder IPC传递过来的ApplicationThread对象(匿名Binder服务端实体端)的Binder通信层代理端BinderProxy(BinderProxy(BpBinder())), 然后借助ATN.asInterface()方法生成新的代理对象ApplicationThreadProxy类型对象app(这个地方有点拗口,如果实在转不过来就算了,我们只需要知道此处获取了ApplicationThread的远程代理端ATP)参数conn: 根据发起端进程传递过来的InnerConnectiond对象(Binder服务端),同样通过转换后,生成IServiceConnection.Stub.Proxy类型对象conn参数service: 数据类型为Intent, 是指本次要启动的service的意图参数callingPackage: 发起方所属的包名参数fl: 是指flags, 此时等于Context.BIND_AUTO_CREATE, 即值为1AMS的担子很大,代码很多,为了的代码的条理性和代码的易阅读性AMS将事务委托给ActiveServices类实例对象进行处理。
前方高能预警,小伙们可以趁着现在上个厕所,喝个水啥的,实在不行休息一下也行!因为后续的代码很长,很长,而且中途还不好休息最好是憋着一口气整完,不然又得找自己究竟在那里了!
//ActiveServices.java int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service, String resolvedType, final IServiceConnection connection, int flags, String callingPackage, final int userId) throws TransactionTooLargeException { ... //查询发起端对应的进程记录结构 final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller); ... ActivityRecord activity = null; //token不为空,代表着发起方具有activiy上下文 if (token != null) { activity = ActivityRecord.isInStackLocked(token); if (activity == null) { Slog.w(TAG, "Binding with unknown activity: " + token); return 0;//存在token, 却找不到activity为空,则直接返回 } } int clientLabel = 0; PendingIntent clientIntent = null; final boolean isCallerSystem = callerApp.info.uid == Process.SYSTEM_UID; if (isCallerSystem) {//调用者是否是system_uid用户 ... } ... //根据发送端所在进程的SchedGroup来决定是否为前台service. final boolean callerFg = callerApp.setSchedGroup != ProcessList.SCHED_GROUP_BACKGROUND; final boolean isBindExternal = (flags & Context.BIND_EXTERNAL_SERVICE) != 0; //根据第三方进程传递进来的Intent信息来检索相对应的服务,这个retriev单词很契合 ServiceLookupResult res = retrieveServiceLocked(service, resolvedType, callingPackage, Binder.getCallingPid(), Binder.getCallingUid(), userId, true, callerFg, isBindExternal);//详见章节3.3.1 ... //查询到相应的Service ServiceRecord s = res.record; if (mAm.mPermissionReviewRequired || Build.PERMISSIONS_REVIEW_REQUIRED) {//权限检测 ... } try { //取消服务的重启调度 if (unscheduleServiceRestartLocked(s, callerApp.info.uid, false)) { if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "BIND SERVICE WHILE RESTART PENDING: " + s); } if ((flags&Context.BIND_AUTO_CREATE) != 0) {//根据前面的分析可知,此处传递过来的flags的值就是Context.BIND_AUTO_CREATE //更新当前service活动时间 s.lastActivity = SystemClock.uptimeMillis(); if (!s.hasAutoCreateConnections()) { // This is the first binding, let the tracker know. ServiceState stracker = s.getTracker(); if (stracker != null) { stracker.setBound(true, mAm.mProcessStats.getMemFactorLocked(), s.lastActivity); } } } mAm.startAssociationLocked(callerApp.uid, callerApp.processName, callerApp.curProcState, s.appInfo.uid, s.name, s.processName); AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);//详见章节3.3.2 //创建对象ConnectionRecord,此处connection来自发起方 ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent); IBinder binder = connection.asBinder();//这里表示IServiceConnection服务的远程代理端,看来Binder真的是很重要阿 ArrayList<ConnectionRecord> clist = s.connections.get(binder); if (clist == null) { clist = new ArrayList<ConnectionRecord>(); s.connections.put(binder, clist); } clist.add(c);// clist是ServiceRecord.connections的成员变量 b.connections.add(c);//b是指AppBindRecord if (activity != null) { if (activity.connections == null) { activity.connections = new HashSet<ConnectionRecord>(); } activity.connections.add(c); } b.client.connections.add(c); if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) { b.client.hasAboveClient = true; } if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { s.whitelistManager = true; } if (s.app != null) { updateServiceClientActivitiesLocked(s.app, c, true); } clist = mServiceConnections.get(binder); if (clist == null) { clist = new ArrayList<ConnectionRecord>(); mServiceConnections.put(binder, clist); } clist.add(c); if ((flags&Context.BIND_AUTO_CREATE) != 0) { s.lastActivity = SystemClock.uptimeMillis(); //启动service,这个过程和startService过程一致 if (bringUpServiceLocked(s, service.getFlags(), callerFg, false, permissionsReviewRequired) != null) {//详见章节3.4 return 0; } } if (s.app != null) {//此时表示service所属进程已经启动 if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { s.app.treatLikeActivity = true; } if (s.whitelistManager) { s.app.whitelistManager = true; } // This could have made the service more important. //更新service所在进程优先级 mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities || s.app.treatLikeActivity, b.client); mAm.updateOomAdjLocked(s.app); } ... if (s.app != null && b.intent.received) { // Service is already running, so we can immediately // publish the connection. try { //Service已经正在运行,则调用InnerConnection的代理对象 c.conn.connected(s.name, b.intent.binder); } catch (Exception e) { Slog.w(TAG, "Failure sending service " + s.shortName + " to connection " + c.conn.asBinder() + " (in " + c.binding.client.processName + ")", e); } ... //当第一个app连接到该binding, 且之前已被bind过, 则回调onRebind()方法 if (b.intent.apps.size() == 1 && b.intent.doRebind) { requestServiceBindingLocked(s, b.intent, callerFg, true); } } else if (!b.intent.requested) { //最终回调onBind()方法 requestServiceBindingLocked(s, b.intent, callerFg, false); } getServiceMap(s.userId).ensureNotStartingBackground(s); } finally { Binder.restoreCallingIdentity(origId); } return 1; }是不是感觉有点懵,这一坨代码的,不要慌!我们理一理这一坨代码的主要功能有哪些:
通常第三方进程调用system_server进程中的Java层Binder服务时候会做一些安全和功能的检测,这里也不例外,会判断发起端进程是否存在,是否存在activity上下文等信息调用retrieveServiceLocked查找根据第三方进程传递进来的Intent所对应的服务,即系统中是否存在符合intent携带消息的Service通过retrieveAppBindingLocked()方法创建AppBindRecord对象,该对象记录着当前ServiceRecord, intent以及发起方的进程ProcessRecord信息一切准备就绪之后,调用bringUpServiceLocked拉起目标服务(如果此时目标进程还没有创建的话,得先创建目标进程)另外可以看到在AMS服务中会将发起端传递过来的connection存储起来,即将发起端进程传递过来的LoadedApk.ServiceDispatcher.InnerConnection的代理对象IServiceConnection.Stub.Proxy类型对象实例connection,保存到新创建的ConnectionRecord对象的成员变量. 再通过clist.add( c ), 将该ConnectionRecord对象添加到clist队列. 后面便可以通过clist来 查询发起方的信息。
前方的代码又是大一坨,主要是牵扯到各种数据结构的转换和关联,得数据结构这得天下啊,看来学好数据结构真的很重要啊,好像跑题了啊!前面我们知道retrieveServiceLocked主要是查询我们的目的端服务ServiceLookupResult结构是否存在(如果前面没有调用过,当然不存在得创建),其查询按照如下步骤依次执行(这里不详细展开了,不然那真是没完没了的了,估计你要疯,我也要疯):
通过userId查询mServiceMap中是否存在对应的ServiceMap实例,如果没有则创建一个并返回赋给ServiceMap实例smap根据服务名从实例对象smap.mServicesByName中查找相应的ServiceRecord,如果没有找到,则往下执行;根据Intent从实例对象smap.mServicesByIntent中查找相应的ServiceRecord,如果还是没有找到,则往下执行;通过PKMS来查询相应的ServiceInfo,如果仍然没有找到相关信息,则不再往下执行,如若找到则继续执行填充ServiceRecord数据结构这里还有一个知识点,就是ServiceInfo.FLAG_EXTERNAL_SERVICE这个标志,是干啥的呢,说实话我也没有用过,我多方翻阅发现这是service在AndroidManifest.xml中新增加的属性android:externalService,当它置为true的时候可以让该Service可以绑定并运行在调用方的App中,而不是在声明这个Service的App中,意不意外惊不惊喜,详细的分析可以参见这个博客android:externalService的功能和原理,这个不是我们的重点不过多分析,我们接着继续往下分析!
接着调用AMS.isSingleton判断我们要查询的目的端Service是否是单例模式,其中符合单例模式需要满足下述三个条件之一:
目的端组件uid>10000,且同时具有ServiceInfo.FLAG_SINGLE_USER flags和INTERACT_ACROSS_USERS权限,说实话这个地方我有个疑问就是目的端进程都没有创建uid怎么就分配了,特殊的进程除外啊目的端组件运行在system进程的情况目的端具有ServiceInfo.FLAG_SINGLE_USER flags,且uid=Process.PHONE_UID或者persistent app的情况如果经过如上一顿猛如虎般的操作之后,依然没有找到合适的ServiceRecord对象,那么则会创建ServiceRecord实例,然后将前面解析,组装出来的相关参数填入ServiceRecord实例对象,然后分别以目的端服务的name和filter为key,存入到smap的mServicesByName和smap的mServicesByIntent。娘的这数据结构,左一坨右一坨的,难受!
说实话看着这一大坨的数据结构对象,真的感觉干不动了。不详细分析了,这里我们需要知道AppBindRecord对象记录着当前ServiceRecord,intent以及发起方的进程信息,并且该记录会存在在ArrayMap对象bindings中存起来,供后续查询!
我们回到章节3.3继续未完成实名之bringUpServiceLocked,这也可以说是本篇的高潮部分了,因为该方法将会拉起服务端Service,倘若服务端进程还没有创建则会先创建进程进而再拉起服务。所以bringUpServiceLocked会分为两种情况处理!
private String bringUpServiceLocked(ServiceRecord r, int intentFlags, boolean execInFg, boolean whileRestarting, boolean permissionsReviewRequired) throws TransactionTooLargeException { //此种情况前提是要启动的Service所属进程已经创建 if (r.app != null && r.app.thread != null) { //兜兜转转最终调用到目的端Service.onStartCommand()过程,这个过程就不分析了,最后通过ApplicationThreadProxy调用到目的端ActivityThread进而控制目的端Service生命周期执行 sendServiceArgsLocked(r, execInFg, false); return null; } if (!whileRestarting && r.restartDelay > 0) { return null;//等待延迟重启的过程,则直接返回 } // 启动service前,把service从重启服务队列中移除 if (mRestartingServices.remove(r)) { r.resetRestartCounter(); clearRestartingIfNeededLocked(r); } //service正在启动,将delayed设置为false if (r.delayed) { getServiceMap(r.userId).mDelayedStartList.remove(r); r.delayed = false; } //确保拥有该服务的user已经启动,否则停止,即多用户情况下 if (mAm.mStartedUsers.get(r.userId) == null) { String msg = ""; bringDownServiceLocked(r); return msg; } try { //服务正在启动,设置package停止状态为false AppGlobals.getPackageManager().setPackageStoppedState( r.packageName, false, r.userId); } catch (RemoteException e) { } catch (IllegalArgumentException e) { ... } //isolated为true表示Service独立运行到一个特定进程中 final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0; final String procName = r.processName; ProcessRecord app; if (!isolated) { //根据进程名和uid,查询ProcessRecord app = mAm.getProcessRecordLocked(procName, r.appInfo.uid, false); if (app != null && app.thread != null) {//此时 try { app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats); //启动服务 realStartServiceLocked(r, app, execInFg); return null; } catch (TransactionTooLargeException e) { throw e; } catch (RemoteException e) { ... } } } else { app = r.isolatedProc; } //对于进程没有启动的情况下,先启动进程 if (app == null && !permissionsReviewRequired) { //启动service所要运行的进程,这就牵涉到Android应用进程创建的流程了 if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags, "service", r.name, false, isolated, false)) == null) {//详见章节3.4.1 bringDownServiceLocked(r);//进程启动失败处理逻辑 return msg; } if (isolated) { r.isolatedProc = app; } } if (!mPendingServices.contains(r)) { mPendingServices.add(r);//注意此处,将ServiceRecord添加到mPendingServices中,后续会遍历此list } if (r.delayedStop) { r.delayedStop = false; if (r.startRequested) { stopServiceLocked(r);//停止服务 } } return null; }前面说过bringUpServiceLocked的启动分为两种情况:
当目标进程已存在,则直接执行realStartServiceLocked(); 当目标进程不存在,则先执行startProcessLocked创建进程, 经过层层调用最后会调用到AMS.attachApplicationLocked, 然后再执行realStartServiceLocked(),这个逻辑我想也是小伙们最关心的, 我们后续从这个地方开撸startProcessLocked的路漫漫其修远兮吾将上下而求索,这个涉及的代码信息量太大,具体流程小伙们可以参见博客Android应用进程创建流程大揭秘,及最终会调用到目的端进程的ActivityThread.main方法里面,其中的过程大概可以使用如下的流程图表示:
我们就这ActivityThread.main接着分析(在这里我们只挑和我们这里相关的,其它的暂且不予关注),代码如下:
//ActivityThread.java public static void main(String[] args) { ... ActivityThread thread = new ActivityThread(); thread.attach(false);//参数false是重点 ... } final ApplicationThread mAppThread = new ApplicationThread(); private void attach(boolean system) { sCurrentActivityThread = this; mSystemThread = system; if (!system) { final IActivityManager mgr = ActivityManagerNative.getDefault(); try { mgr.attachApplication(mAppThread);//老套路了啊,远程调用AMS的attachApplication方法,注意这里的参数是IApplicationThread的Binder实体端,该调用流程详见3.4.2 } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } else {//不要问我谁会走此分支,我不会告诉你system_server的创建会走此分支的 ... } }到这里了我们就不详细分析目的端进程怎么通过Binder框架调用到AMS服务来了,大概的调用流程如下:
AMP.attachApplication() ---> BinderProxy.transact() ---> BpBinder.transact()---> binder驱动传输---> JavaBBinder.onTransact()---> AMN.onTransact()---> AMS.scheduleCreateService()这个前面章节有具体分析过了,举一反三即可。我们直捣黄龙直接分析AMS的attachApplication方法。
//ActivityMangerService.java @Override public final void attachApplication(IApplicationThread thread) { synchronized (this) { ... attachApplicationLocked(thread, callingPid); ... } } private final boolean attachApplicationLocked(IApplicationThread thread, int pid) { ... if (!badApp) { try { //寻找所有需要在该进程中运行的服务 didSomething |= mServices.attachApplicationLocked(app, processName);//详见3.4.3 checkTime(startTime, "attachApplicationLocked: after mServices.attachApplicationLocked"); } catch (Exception e) { Slog.wtf(TAG, "Exception thrown starting services in " + app, e); badApp = true; } } ... }好吗又调用到了ActiveServices对象实例中去了,我们接着分析其attachApplicationLocked方法。
兜兜转转,又来到了realStartServiceLocked方法,这个和前面章节总结的前后照应了:
即当需要创建新的进程时候,会调用AMS.startProcessLocked启动进程,然后经过千辛万苦的各种辗转最后调用到attachApplicationLocked方法,进而调用realStartServiceLocked方法若不用创建进程,则直接调用realStartServiceLocked方法,进行剩余工作记得我们经常说的Android四大组件中不能执行耗时操作吗,小伙们有想过为什么吗?这是因为有Android ANR的预埋雷机制,关于ANR的机制和原理可以详细参见博客理解Android ANR的触发原理,在bumpServiceExecutingLocked会发送一个延迟处理的消息SERVICE_TIMEOUT_MSG。在方法scheduleCreateService执行完成,也就是onCreate回调执行完成之后,便会remove掉该消息。但是如果没能在延时时间之内remove该消息,则会进入执行service timeout流程触发ANR,这就是为啥Android 四大组件不能执行耗时操作的原因。
不管选择那个分支,最终都会走入scheduleServiceTimeoutLocked方法!
还记得我们老说的Service的ANR超时时间吗,什么前台服务是多少,后台服务是多少,一切的源头都在于此,这里会延时发送消息SERVICE_TIMEOUT_MSG,而这个延时的时间可以看到分为两种情况:
对于前台服务,则超时时间为SERVICE_TIMEOUT,即timeout=20s对于后台服务,则超时间为SERVICE_BACKGROUND_TIMEOUT,即timeout=200s注意这里的mAm.mHandler是在AMS中进行处理的,其处理逻辑如下所示:
//AMS.java final class MainHandler extends Handler { @Override public void handleMessage(Message msg) { switch (msg.what) { ... case SERVICE_TIMEOUT_MSG: { if (mDidDexOpt) { mDidDexOpt = false; Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG); nmsg.obj = msg.obj; mHandler.sendMessageDelayed(nmsg, ActiveServices.SERVICE_TIMEOUT); return; } mServices.serviceTimeout((ProcessRecord)msg.obj);//又调用到ActiveServices中去了,老子不跟了 } break; ... } } }历史和其的相似,看来在Android源码的学习过程中Binder是一个必须攻克的堡垒啊。这里的ATP是IApplicationThread服务的代理端,然后借助BInder框架会调用到其服务端,而该匿名Binder服务端注册在目的端进程中,其调用过程大概如下:
ATP.scheduleCreateService()---> BinderProxy.transact() ---> BpBinder.transact()---> binder驱动传输---> JavaBBinder.onTransact()---> ATN.onTransact()---> ATN.attachApplication()通过ATP的努力和我们的Binder框架的协助,我们跨越万水千山,完成了system_server所在进程到Service所在目的端进程调用过程,让我们接着分析看看目的端进程是怎么处理bindService的RPC请求的。我好难啊!
无需多言,直接进入下一关,打怪升级!
巧用ActivityThread的主线程的Handler发送消息,这里我们可以总结一下规律,通常AMS通过ATP发送过来的消息,遵循如下的处理逻辑,如下:
scheduleXXX() ---> handleXXX()无需多言,直接进入下一关,打怪升级!
该段代码,主要执行两个业务逻辑:
执行目的端Service的onCreate方法调用AMS的serviceDoneExecuting方法,解除在前面3.5.2章节预埋的ANR雷,不然真的会爆炸的我曾跨过山河大海也穿过人山人海,终于来到了目标进程Service的生命周期onCreate方法中,通常Service的子类会重写该方法!不容易啊,终于看到了胜利曙光,但是还没有结束,依然还需要战斗!
不要问我为啥来到此处,这都是套路,慢慢的套路!
//AMS.java public void serviceDoneExecuting(IBinder token, int type, int startId, int res) { synchronized(this) { if (!(token instanceof ServiceRecord)) { Slog.e(TAG, "serviceDoneExecuting: Invalid service token=" + token); throw new IllegalArgumentException("Invalid service token"); } //AMS很闲啊,啥也不干全部扔给了ActivityServices处理了,详见4.5.1 mServices.serviceDoneExecutingLocked((ServiceRecord)token, type, startId, res); } }无需多言,直接进入下一关,打怪升级!
我感觉Android的开发工程师也很难受,一个方法里面代码太多了吗,小伙们说这个裹脚布,搞方法太多了吗,跳转的难受!
在此处终于将执行removeMessages将3.5.2章节预埋的ANR雷移除,即将消息息SERVICE_TIMEOUT_MSG从Handler的消息队列中移除。分析至此,小伙们应该了解了Service启动过程出现ANR,”executing service [发送超时serviceRecord信息]”,这往往是service的onCreate()回调方法执行时间过长的原因了。
此时的我在那里,该何处何从。此时让我们穿越时空重新回到system_server进程,继续章节3.5之未完成实名,革命仍未成功还需努力啊。
通过bindService方式启动的服务, 那么该serviceRecord的bindings则一定不会空,不然后续的工作咋开展呢!而这里的入参ServiceRecord实例对象的创建可以回到章节3.5温故温故。接着继续调用重载的requestServiceBindingLocked方法继续处理,而在该重载的方法中主要执行了两项操作:
调用bumpServiceExecutingLocked方法继续埋雷,倘若目标Service的onBind方法没有在指定的时间内执行完毕,则会触发ANR,这个已经在前面有过详细分析了接着调用目标服务端进程的AT代理端ATP的方法scheduleBindService,向AT发送Binder请求AMS和AT之间的交互很频繁啊,目的端进程和system_server进程之间通过IApplicationThread和IActivityManager来回的折腾着。这里的ATP是IApplicationThread服务的代理端,然后借助BInder框架会调用到其服务端,而该匿名Binder服务端注册在目的端进程中,其调用过程大概如下:
ATP.scheduleBindService()---> BinderProxy.transact() ---> BpBinder.transact()---> binder驱动传输---> JavaBBinder.onTransact()---> ATN.onTransact()---> ATN.scheduleBindService()这么来回的跳转,折腾着感觉心好累啊!但是木有办法,要想深入只能是操起锤子接着干了。
无需多言,直接进入下一关,打怪升级!
巧用ActivityThread的主线程的Handler发送消息,这里我们可以总结一下规律,通常AMS通过ATP发送过来的消息,遵循如下的处理逻辑,如下:
scheduleXXX() ---> handleXXX()无需多言,直接进入下一关,打怪升级!
该段代码,主要执行两个业务逻辑:
执行目的端Service的onBind方法调用AMS的publishService方法,即将获取到Service中的匿名Binder实体通过Binder传递到AMS中我曾跨过山河大海也穿过人山人海,终于来到了目标进程Service的生命周期onBind方法中,Service的onBind()是抽象方法, 所以大家创建Service子类时必须要覆写该方法, 返回IBinder对象, 也可以直接返回NULL.
AMS和AT之间的交互很频繁啊,目的端进程和system_server进程之间通过IApplicationThread和IActivityManager来回的折腾着。这里的AMP是IActivityManager服务的代理端,然后借助BInder框架会调用到其服务端AMS,其调用过程大概如下:
AMP.publishService()---> BinderProxy.transact() ---> BpBinder.transact()---> binder驱动传输---> JavaBBinder.onTransact()---> AMN.onTransact()---> AMN.publishService()是时候展现真正的实力了,怎么从AMP到AMS的过程我们就不细说了,因为本篇博客里面已经有很多次这种操作了。我们直奔主题进入AMS。
远程服务的onBind()在目的服务端进程的返回值的IBinder类型是(Bn端), 在AMP.publishService()过程中经过data.writeStrongBinder(service)传递到底层驱动在system_server内核的binder_proc中创建上述IBinder实体的binder_ref引用, 再回到system_server进程中AMN.onTransact()中经过data.readStrongBinder()方法会获取该service所相对应的代理对象(Bp端).总而言之,此处的IBinder类型的service就是远程服务进程中的Bp端对象,并且此处的IBinder类型的service就是一个典型的匿名Binder,它并没有向servicemanager进程注册。
此处是关键,让我们先缓一缓。这里先取出ServiceRecord 中的ConnectionRecord列表,然后匹配对应的intent消息,如果匹配到则取出ConnectionRecord中的IServiceConnection实例对象conn,先换缓缓,这个ServiceRecord和ConnectionRecord又是啥时候创建的呢,这个就要回到章节3.3和章节3.3.1其中涉及到了ServiceRecord和ConnectionRecord创建。
还有一个核心就是这里的c.conn指代什么呢,关于此处就得回到2.3章节细品了,这里我们给出结论c.conn是指通往发起端进程的IServiceConnection.Stub.Proxy代理对象,其是经由Binder调用从发起端进程传递过来的,而我们此时通过该代理端通过Binder IPC调用到进入发起方进程的IServiceConnection.Stub实体中。关于该匿名Binder的传递路线如下所示:
发起端进程(IServiceConnection.Stub) ---> Binder驱动 ---->system_server进程(IServiceConnection.Stub.Proxy)并且根据前面的分析可知,由于LoadedApk.ServiceDispatcher.InnerConnection 继承于IServiceConnection.Stub. 所以,接下来便由回到发起方进程中的InnerConnection对象,关于其调用过程这里简单阐述一下:
IServiceConnection.Stub.Proxy.connected()---> BinderProxy.transact() ---> BpBinder.transact()---> binder驱动传输---> JavaBBinder.onTransact()---> IServiceConnection.Stub.onTransact()---> InnerConnection.connected()来返往复,曲折之路告一段落,我们终于返回了发起端进程。让我们接着来到发起端进程的LoadedApk.ServiceDispatcher.InnerConnection分析之,关于LoadedApk可以回过头到2.3.1章节看看其类图,再巩固巩固!
无需多言,直接进入下一关,打怪升级!
无需多言,直接进入下一关,打怪升级!
关于Handerl.post用法就不用多讲了,最后会触发RunConnection 的run方法执行,这里我们要注意doConnected方法的两个入参:
mName指代我们bind的目的端远程Service的组件名对象ComponentName 实例mService指代我们bind的目的端远程端服务onBind()返回的返回的Binder实体的代理端对象到此处分析起来也比较简单了,主要执行了如下两个逻辑:
先行创建死亡监听对象,也是内部类:LoadedApk.ServiceDispatcher.DeathMonitor,监听目的端服务Binder的情况调用用户传递过来的回调类ServiceConnection的onServiceConnected方法此处不在本文的分析的重点之内,总之这里会监听目的服务端的情况,如果其发生了异常则会通知到其bind的发起端!关于DeathMonitor强烈推荐参见博客Binder死亡通知机制之linkToDeath
至此,bindService已经基本分析完毕了,是时候重新拿去时序图来重新总结一把了,时序图如下:
小伙伴门有没有发现在bindService中牵涉到了大量的Binder IPC,在Android的源码分析里真的是学好Android Binder很重要啊,并且在交互过程中牵涉到了好几处的匿名Binder,小伙们能找出来吗并且关于上述时序图中,红色代表Binder通信,绿色类代表发起端进程持有,黄色类代表system_server进程持有,蓝色代类代表目的端进程持有还记得我们一开篇就说的匿名/实名Binder吗,综管全篇不知小伙们发现没有其实bindService的实质就是匿名Binder跨进程的传递,当然也不全是毕竟还是目的端进程创建的一些操作呢!那么我们站在匿名Binder传递的角度来看看bindService的流程,其最最核心的一点就是目的端Service IBinder匿名BInder服务端怎么传递到发起端,其传递过程大概如下:
目的端Service(IBinder实体) ----> Binder驱动传递 ---> system_server进程(IBinder实体对应的代理端) ---> 发起端进程(IBinder实体对应的代理端)经过此役我想小伙们一定意识到了Android Binder在Android的整个源码界的分量,想深入分析Android框架Binder是一道必须越过的坎。希望小伙们能越过Android Binder这道坎,在Android的道路上越走越远。好了,关于Android Binder框架实现之bindService详解到这里真的告一段落了,如果小伙们喜欢可以关注或者给我点个赞。最后特别鸣谢不在Android界的大神gityuan,因为是他给我了前进的力量!
