Android Binder原理
以前看源码经常会看到Binder的东西,比如AMS,ActivityThread等,之前虽然对Binder有所了解,但也是模模糊糊的,这次终于下定决心好好的弄一弄它的原理,揭开它头上的那块面纱。
首先,Binder主要是Android 跨进程通信的一种方式,它是一个Android 中的一个类,实现了IBinder接口,主要是用在Service中。Android 跨进程通信有几种方式,Bundle、AIDL、Messenger、ContentProvider、socket等。而AIDL跟Messenger内部都是用的是Binder机制。下面就来分析一下Binder的工作机制。
首先我们需要建立三个类,一个是实体类User.kt, 继承Percelable接口
以及User.aidl, 里面定义了
parcelable User
IUserManager.aidl 定义了服务器端可调用的接口
interface IUserManager{
List<User> getUserList();
void addUser(in User user)
}
然后build 工程,系统会在build路径下自动生成IuserManager.java文件,我们具体介绍一下里面的方法。
Binder的唯一标识,一般用类名表示
用于将服务端的Binder对象转换成客户端aidl接口类型的对象,如果客户端跟服务器端在同一个进程里面,返回的是服务器端本身,否则返回的是内部定义的Stub.Proxy()对象
该方法运行在服务器端的binder线程池中,通过code可以确定客户端请求的目标方法,然后从data中取出目标方法所需要的参数,然后再执行目标方法,执行完毕后,把结果写入reply中,如果return false,客户端请求会失败,可以用来做一些权限校验。
该方法运行在客户端,首先用android.os.Parcel.obtain()创建两个Parcel 对象 data(输入), reply(输出),如果该方法有参数的话,把参数写入data,
data.writeInt(1)
user.writeToParcel(data,0)
然后调用服务端的transact方法来触发RPC请求,mRemote.transact(),同时当前线程挂起,然后服务端的onTransact方法会触发,直到RPC过程返回,当前线程继续执行,从reply中获取返回结果。
有两个地方需要注意,首先客户端发起远程请求时,客户端线程会被挂起,所以这个操作可能是一个耗时操作,不能在UI线程中发起请求。其次,服务端的Binder方法是运行在Binder线程池中,所以Binder方法不管是否耗时,都应该用同步的方式去实现。
另外,在进程间通信的时候,我们的binder有可能会断开,这个时候我们需要设置一个死亡代理。我们定义一个DeathRecipient对象,里面有一个回调方法binderDied(), 当Binder死亡的时候该方法会回调,然后我们可以调用unlinkToDeath()移除之前的代理,并重新绑定远程service
binder.linkToDead(mDeathRecipient,0)
还有方法isBinderAlive()也可以判断binder是否死亡。
最后,我们用AIDL中,经常需要做一些权限的校验。具体有两种方法,一种是permission校验,我们可以定义一种permission, 并且在onBind()方法中调用checkCallingOrselfPermission()对permission进行校验,客户端想绑定我们的服务必须在AndroidMenifest.xml文件中加上这个权限。
第二种方法,我们可以在onTransact方法中处理。可以用第一种方法的permission校验,也可以采用uid和pid进行校验,验证不通过,直接返回false就可以了。