前言:
当前兄弟们对“bind绑定器”大概比较关切,同学们都需要知道一些“bind绑定器”的相关资讯。那么小编在网络上搜集了一些有关“bind绑定器””的相关知识,希望兄弟们能喜欢,大家一起来学习一下吧!前言:
查看源码时,切记要把持好自己的方向和尺度,忌刨根问底,最好是能联系已有的思维,这样才能快速看完想要看的源码。
正如标题说的,我们今天要干的事是就是查看 bindService() 源码。
为什么要查看这个方法的源码呢?
关于这个问题,其实我们可以想一下,我们在开发过程中可能会用到 PackageManagerService、ActivityManagerService、InputManagerService 等系统服务,这些系统服务是 Android 系统启动时就被初始化完成,他们彼此存在于单独的进程当中,当我们使用这些系统服务的时候,就会 “跨进程” 去访问他们。
而在获取到这些系统服务的过程中,SystemService 这个系统服务发挥了很重要的作用,他就像一个平台一样,帮助我们得到我们想要的系统服务,然后完成我们想要完成的操作。
这个时候,我们想一下,上一篇文章中编写的 AIDL 实例,还记得我们是如何从 Client 端绑定到 Server 端的吗?
Server 端是一个 service ,我们想要使用 Server 端的逻辑,需要先绑定服务即 bindService() ,然后开始通过上图中第一步 asInterface() 拿到服务端的代理 Proxy,然后才开始进行我们想要的操作,所以 bindService() 是我们实现 binder 跨进程通信的出发点。
此外我们使用系统服务,其出发点也是 bindService(),我们在使用 bindService() 时,本质也是在使用 ActivityManagerService 的 bindService() 方法,所以研究这个 bindService() 方法本身就是跨进程通信。
好了,废话不多说了,我们开始吧。
① 从我们的实例 bindService() 方法开始
进入 ContextWrapper 中的 bindService()
这里需要提一下,ContextWrapper 是 Context 类的包装实现类,而Context的另一个实现类是 功能实现类 ContextImpl 类。
继续点击,进入 Context 的 bindService() 方法
但是看到没有,这里面bindService() 方法是抽象方法,所以我们需要跳到 Context 的功能实现类去
进入 ContextImpl 的 bindService() 方法
其中 warnIfCallingFromSystemProcess() 是判断是否是由系统内部调用的,我们这里直接忽略,看下方的 bindServiceCommon() 逻辑。
绿框的是一些安全校验,红框的是我们重点要看的。
ActivityManagerNative.getDefault().bindService();
进入 ActivityManagerNative.getDefault()
那么 gDefault 是什么呢?
而 Singleton.java 是这么定义的
分析:gDefault 是一个Singleton 类型的类,通过 create() 方法创建出对象,然后通过 get() 方法返回。
查看 Singleton 方法的 create() 内部
IBinder b = ServiceManager.getService("activity");
此方法返回了一个 IBinder 对象。
查看 ServiceManager 中的 getService() 方法
返回的 IBinder 对象是通过 sCache 中查到对应的元素返回的
而 sCache 是这样的
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
是一个由名字和 IBinder 为 key、value 的 Map 。
sCache 的初始化时是程序启动时自动初始化的
所以,我们可以先理解为 这里就是拿到了一个 key 为 "activity" 的 IBinder 对象。
再来看
IActivityManager am = asInterface(b);
asInterface() 这个方法是不是很熟悉,我们上一篇文章中就是通过
asInterface() 方法从客户端获取到 aidl 的 Proxy 代理类,进而访问到服务端代码的。
这里其实也是相通的。
那么 IActivityManager 是什么呢?
继承自 IInterface ,所以我们可以推理出他就是一个 aidl 文件,这个就跟我们定义好 aidl 文件之后,通过 make project 得到了Android Studio 为我们生成的 aidl 的 “.java” 文件是一样的原理。
我们先来看一下我们上篇文章的 aidl 文件的结构 IMyAidlInterface.java 文件
这个生成的 aidl 的 .java 文件既包括了 Stub 类,也包括了 Proxy 类。
并且在生成的 .java 文件的 Stub 和 Proxy 都实现了 aidl 的接口,如下
好了,到这里,我们再来看一下系统给我们提供的 ActivityManager 的 aidl 的 .java 文件
他就是一个单纯的 interface 接口文件,内部并没有定义 Stub 类和 Proxy 类。
而我们想看的是
IActivityManager am = asInterface(b);
实际上就是 IActivityManager 的实现类
根据类的关系,我们知道 ActivityManagerNative 就是 IActivityManager 的 Stub 类,所以
IActivityManager am = asInterface(b);
会调用到这个地方
ActivityManagerNative.asInterface(b);
作用就是得到 ActivityManager 的代理 ActivityManagerProxy 类对象。
那么续上步
ActivityManagerNative.getDefault().bindService();
实际上就是
ActivityManagerProxy.bindService();
那么就很清晰了,此时 ActivityManagerProxy 像上篇文章一样通过 transact 方法调用到 Stub 中的方法,进而连接到真正的 Server 端的处理逻辑,我们继续看。
调用
mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
看到这里,回想一下上篇文章中,我们分析过 Proxy 调用 transact 方法,会调用 Stub 端的 onTransact() 方法,所以我们今天就不分析过程了,直接跳到 Stub 端的 onTransact()。
onTransact() 方法 会对传入的 code 进行判断
由于我们传入的是 BIND_SERVICE_TRANSACTION
所以我们直接跳到对应的位置:
可以看到 Stub 调用了bindService 的方法,由上篇文章的分析我们知道,这个时候 Stub 会直接调用 Server 端的逻辑了,我们直接跳到 ActivityManagerNative 的实现类 ActivityManagerService。
找到对应的 bindService() 方法
忽略前面的校验,我们看到这个方法
mServices.bindServiceLocked();
mServices 类型就是 ActiveServices,好,我们进入
进入 ActiveServices 中的 bindServiceLocked() 方法
这个方法很长,截取主要逻辑:
我们先看第一个红框中的关键方法
// 创建 ServicebringUpServiceLocked()
方便起见,直接定位到关键代码
// 当 app 启动时,创建 Service 逻辑realStartServiceLocked()
我们继续进入
红框内为关键代码
app:
进入
定位到 thread:
可以看出这又是一个 aidl ,红框内
scheduleCreateService();
是创建 Service 的方法,而这个也是通过 aidl 完成访问的,这里 aidl 的调用过程就不再详述了,直接列出调用逻辑
IApplicationThread -> aidl接口;IApplicationThread aidl 文件的 Stub -> ApplicationThreadNative;IApplicationThread aidl 文件的 Proxy -> ApplicationThreadProxy;IApplicationThread aidl 文件(ApplicationThreadNative)他的 Stub 的实现类 -> ActivityThread.ApplicationThread(是个内部类)
所以我们直接定位到
ApplicationThreadProxy.scheduleCreateService()
进而定位到 Stub 的 onTransact(),这里对应的就是
ApplicationThreadNative.onTransact();
这个方法中的
scheduleCreateService()
即为真正创建 Service 的方法,也就是 Stub 的实现类处理的。
也就是
ActivityThread.ApplicationThread.scheduleCreateService();
而其内部又是由
sendMessage(H.CREATE_SERVICE,s);
实现的。那么我们应该去 Handler 中去找他的处理方法了。
找到对应的
handleMessage()方法
定位到
handleCreateService();
到这里,我们想要连接的 Service 就通过类加载器创建 并且启动了,此实例也被存放到mServices中方便后续的创建和绑定(服务只能创建和绑定一次)。
回想一下,我们在上篇文章中,仅仅是将 Server 端安装到了手机上,并未开启的情况下,就能通过 Client 端 “绑定并且启动” ,就是因为“服务端的Service” 是通过类加载器的方法创建出来的。
好,我们继续看
第二个红框的方法 ActiveServices.requestServiceBindingLocked();
ActiveServices.requestServiceBindingLocked();
既然第一个方法是创建并且启动 Service,那么这个方法应该就是绑定 Service 的处理了。
其他逻辑先忽略我们直接进入主要的逻辑
r.app.thread.scheduleBindService();
仍然是创建 Service 类内部的逻辑
进入
再进入
再进入 handleMessage()
再进入
红框内区分了两种情况:未绑定、已绑定。
如果未绑定,那么会调用
IBinder binder = s.onBind(data.intent);ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder);
否则会调用
s.onRebind();
我们先看第一个
// 通过 intent 获取到服务端 onBind()的返回值 IBinderIBinder binder = s.onBind(data.intent);
然后将此服务端的 IBinder 对象通过下方代码传递给客户端
ActivityManagerNative.getDefault().publishService(data.token, data.intent, binder);
好,我们深入一下看看
ActivityManagerNative.getDefault();
上文已经分析过,此方法会直接调用 ActivityManagerService 的方法:
跳转
内部的逻辑在这里
//连接我们传递过来的 Service(服务端的 IBinder)c.conn.connected(r.name, service);
c: ConnectionRecord
conn:
再进入 IServiceConnection 发现他也是一个 aidl 文件
通过关联搜索定位到 IServiceConnection 的引用位置
发现
InnerConnection 是 IserviceConnection.Stub 的实现类,也就是真正IServiceConnection 的 connected 方法的实现。
sd 的 connected(); 方法
doConnected 方法
所以,我们推论最终的绑定服务的实现是这样的
c.conn.connected(r.name, service);==>MainActivity 中的 onServiceConnected()
其实我们从MainActivity 中的 bindService 追踪 conn 的路径也能得到这个结论。
MainActivity.bindService(intent, conn, BIND_AUTO_CREATE); ContextWrapper.bindService(Intent service, ServiceConnection conn, int flags);ContextImpl.bindService(Intent service, ServiceConnection conn, int flags);ContextImpl.bindServiceCommon(Intent service, ServiceConnection conn, int flags, UserHandle user);
进入
此方法最终会调用到 MainActivity 中 ServiceConnetion 的回调
onServiceConnected(ComponentName name, IBinder service)
这样,我们就把 bindService 从开始到结束变成一个环路,这个过程其实就是 Binder 机制。
好了,绕了这么大圈,其实简单总结一下就是这样的。
抽空画了个流程图,方便梳理过程:
标签: #bind绑定器