最近一段时间因为工做,接触到framework部分比较多一点,也不免要和Binder打一些交道,也整理了一些相关知识,但准备写这篇文章时,仍是有些慌。并且关于整个Binder机制的复杂程度不是三言两语能描叙清楚的,也惧怕本身的理解有些误差,误导一些朋友(ps:反正也没人看....扎心)因此也参考了不少资料。html
本文主要站在Android开发的角度来大体解析下Binder在java层的一些知识原理,给你们脑子造成一个完整的概念,好比AIDL的实现原理,Binder是怎么通讯的等等,文章文字较多,请耐心观看java
熟悉的朋友能够看看下篇,将介绍Activity的启动流程以及Android中的Hook技术:linux
Android是基于Linux内核的,因此Android要实现进程间的通讯,其实大可以使用linux原有的一些手段,好比管道,共享内存,socket等方式,可是Android仍是采用了Binder做为主要机制,说明Binder具备无可比拟的优点。安全
其实进程通讯大概就两个方面因素,一者性能方面,传输效率问题,传统的管道队列模式采用内存缓冲区的方式,数据先从发送方缓存区拷贝到内核开辟的缓存区中,而后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程,而socket都知道传输效率低,开销大,用于跨网络进程交互比较多,共享内存虽然无需拷贝。bash
两者这是安全问题,Android做为一个开放式,拥有众多开发者的的平台,应用程序的来源普遍,确保终端安全是很是重要的,传统的IPC通讯方式没有任何措施,基本依靠上层协议,其一没法确认对方可靠的身份,Android为每一个安装好的应用程序分配了本身的UID,故进程的UID是鉴别进程身份的重要标志,传统的IPC要发送相似的UID也只能放在数据包里,但也容易被拦截,恶意进攻,socket则须要暴露本身的ip和端口,知道这些恶意程序则能够进行任意接入。服务器
综上所述,Android须要一种高效率,安全性高的进程通讯方式,也就是Binder,Binder只须要一次拷贝,性能仅次于共享内存,并且采用的传统的C/S结构,稳定性也是没得说,发送添加UID/PID,安全性高。网络
咱们知道进程之间是没法直接进行交互的,每一个进程独享本身的数据,并且操做系统为了保证自身的安全稳定性,将系统内核空间和用户空间分离开来,保证用户程序进程崩溃时不会影响到整个系统,简单的说就是,内核空间(Kernel)是系统内核运行的空间,用户空间(UserSpace)是用户程序运行的空间。为了保证安全性,它们之间是隔离的,因此用户空间的进程要进行交互须要经过内核空间来驱动整个过程。socket
Binder是基于C/S机制的,要实现这样的机制,server必须须要有特定的节点来接受到client的请求,也就是入口地址,像输入一个网址,经过DNS解析出对应的ip,而后进行访问,这个就是server提供出来的节点地址,而Binder而言的话,与传统的C/S不太同样,Binder自己来做为Server中提供的节点,client拿到Binder实体对象对应的地址去访问Server,对于client而言,怎么拿到这个地址并创建起整个通道是整个交互的关键所在,并且Binder做为一个Server中的实体,对象提供一系列的方法来实现服务端和客户端之间的请求,只要client拿到这个引用就能够或者一个有着该方法代理对象的引用,就能够进行通讯了。ide
引用Android Bander设计与实现 - 设计篇一段话来总结:
面向对象思想的引入将进程间通讯转化为经过对某个Binder对象的引用调用该对象的方法,而其独特之处在于Binder对象是一个能够跨进程引用的对象,它的实体位于一个进程中,而它的引用却遍及于系统的各个进程之中。最诱人的是,这个引用和java里引用同样既能够是强类型,也能够是弱类型,并且能够从一个进程传给其它进程,让你们都能访问同一Server,就象将一个对象或引用赋值给另外一个引用同样。Binder模糊了进程边界,淡化了进程间通讯过程,整个系统仿佛运行于同一个面向对象的程序之中。形形色色的Binder对象以及星罗棋布的引用仿佛粘接各个应用程序的胶水,这也是Binder在英文里的原意。
Binder基于C/S的结构下,定义了4个角色:Server、Client、ServerManager、Binder驱动,其中前三者是在用户空间的,也就是彼此之间没法直接进行交互,Binder驱动是属于内核空间的,属于整个通讯的核心,虽然叫驱动,可是实际上和硬件没有太大关系,只是实现的方式和驱动差很少,驱动负责进程之间Binder通讯的创建,Binder在进程之间的传递,Binder引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
ServerManager的做用?
咱们知道ServerManager也是属于用户空间的一个进程,主要做用就是做为Server和client的桥梁,client能够ServerManager拿到Server中Binder实体的引用,这么说可能有点模糊,举个简单的例子,咱们访问,www.baidu.com,百度首页页面就显示出来了,首先咱们知道,这个页面确定是发布在百度某个服务器上的,DNS经过你这个地址,解析出对应的ip地址,再去访问对应的页面,而后再把数据返回给客户端,完成交互。这个和Binder的C/S很是相似,这里的DNS就是对应的ServerManager,首先,Server中的Binder实体对象,将本身的引用(也就是ip地址)注册到ServerManager,client经过特定的key(也就是百度这个网址)和这个引用进行绑定,ServerManager内部本身维护一个相似MAP的表来一一对应,经过这个key就能够向ServerManager拿到Server中Binder的引用,对应到Android开发中,咱们知道不少系统服务都是经过Binder去和AMS进行交互的,好比获取音量服务:
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
细心的朋友应该发现ServerManager和Server也是两个不一样的进程呀,Server要向ServerManager去注册不是也要涉及到进程间的通讯吗,当前实现进程间通讯又要用到进程间的通讯,你这不是扯犊子吗....莫急莫急,Binder的巧妙之处在于,当ServerManager做为Serve端的时候,它提供的Binder比较特殊,它没有名字也不须要注册,当一个进程使用BINDER_SET_CONTEXT_MGR命令将本身注册成SMgr时Binder驱动会自动为它建立Binder实体,这个Binder的引用在全部Client中都固定为0而无须经过其它手段得到。也就是说,一个Server若要向ServerManager注册本身Binder就必需经过0这个引用号和ServerManager的Binder通讯,有朋友又要问了,server和client属于两个不一样的进程,client怎么能拿到server中对象,不妨先看看下面的交互图
从上图很清晰的能够看出来整个的交互过程,本来从SM中拿到binder的引用,经过Binder驱动层的处理以后,返回给了client一个代理对象,实际上若是client和server处于同一个进程,返回的就是当前binder对象,若是client和server不处于同一个进程,返回给client的就是一个代理对象,这一点,有兴趣能够看下源码。
Binder本质上只是提供了一种通讯的方式,和咱们具体要实现的内容没有关系,为了实现这个服务,咱们须要定义一些接口,让client可以远程调用服务,由于是跨进程,这时候就要设计到代理模式,以接口函数位基准,client和server去实现接口函数,Server是服务真正的实现,client做为一个远程的调用。
上面说了那么多,你们伙也看累了,下面经过代码的形式让你们对Binder加深点理解,平常开发中,涉及到进程间通讯的话,咱们首先想到的可能就是AIDL,但不知道有没有和我感受同样的朋友。。第一次写AIDL是蒙蔽的,经过.aidl文件,编译器自动生成代码,生成一个java文件,里面又有Stub类,里面还有Proxy类,彻底不理解里面的机制,确实不便于咱们理解学习,为了加深理解,咱们抛弃aidl,手写一个通讯代码。
首先咱们要定义一个接口服务,也就是上述的服务端要具有的能力来提供给客户端,定义一个接口继承IInterface,表明了服务端的能力
public interface PersonManger extends IInterface {
void addPerson(Person mPerson);
List<Person> getPersonList();
}
复制代码
接下来咱们就要定义一个Server中的Binder实体对象了,首先确定要继承Binder,其次须要实现上面定义好的服务接口
public abstract class BinderObj extends Binder implements PersonManger {
public static final String DESCRIPTOR = "com.example.taolin.hellobinder";
public static final int TRANSAVTION_getPerson = IBinder.FIRST_CALL_TRANSACTION;
public static final int TRANSAVTION_addPerson = IBinder.FIRST_CALL_TRANSACTION + 1;
public static PersonManger asInterface(IBinder mIBinder){
IInterface iInterface = mIBinder.queryLocalInterface(DESCRIPTOR);
if (null!=iInterface&&iInterface instanceof PersonManger){
return (PersonManger)iInterface;
}
return new Proxy(mIBinder);
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code){
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSAVTION_getPerson:
data.enforceInterface(DESCRIPTOR);
List<Person> result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(result);
return true;
case TRANSAVTION_addPerson:
data.enforceInterface(DESCRIPTOR);
Person arg0 = null;
if (data.readInt() != 0) {
arg0 = Person.CREATOR.createFromParcel(data);
}
this.addPerson(arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
@Override
public IBinder asBinder() {
return this;
}
}
复制代码
首先咱们看asInterface方法,Binder驱动传来的IBinder对象,经过queryLocalInterface方法,查找本地Binder对象,若是返回的就是PersonManger,说明client和server处于同一个进程,直接返回,若是不是,返回给一个代理对象。
固然做为代理对象,也是须要实现服务接口
public class Proxy implements PersonManger {
private IBinder mIBinder;
public Proxy(IBinder mIBinder) {
this.mIBinder =mIBinder;
}
@Override
public void addPerson(Person mPerson) {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (mPerson != null) {
data.writeInt(1);
mPerson.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mIBinder.transact(BinderObj.TRANSAVTION_addPerson, data, replay, 0);
replay.readException();
} catch (RemoteException e){
e.printStackTrace();
} finally {
replay.recycle();
data.recycle();
}
}
@Override
public List<Person> getPersonList() {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
List<Person> result = null;
try {
data.writeInterfaceToken(DESCRIPTOR);
mIBinder.transact(BinderObj.TRANSAVTION_getPerson, data, replay, 0);
replay.readException();
result = replay.createTypedArrayList(Person.CREATOR);
}catch (RemoteException e){
e.printStackTrace();
} finally{
replay.recycle();
data.recycle();
}
return result;
}
@Override
public IBinder asBinder() {
return null;
}
}
复制代码
这里的代理对象实质就是client最终拿到的代理服务,经过这个就能够和Server进行通讯了,首先经过Parcel将数据序列化,而后调用 remote.transact()将方法code,和data传输过去,对应的会回调在在Server中的onTransact()中
而后是咱们的Server进程,onBind方法返回mStub对象,也就是Server中的Binder实体对象
public class ServerSevice extends Service {
private static final String TAG = "ServerSevice";
private List<Person> mPeople = new ArrayList<>();
@Override
public void onCreate() {
mPeople.add(new Person());
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
private BinderObj mStub = new BinderObj() {
@Override
public void addPerson(Person mPerson) {
if (mPerson==null){
mPerson = new Person();
Log.e(TAG,"null obj");
}
mPeople.add(mPerson);
Log.e(TAG,mPeople.size()+"");
}
@Override
public List<Person> getPersonList() {
return mPeople;
}
};
}
复制代码
最终咱们在客户端进程,bindService传入一个ServiceConnection对象,在与服务端创建链接时,经过咱们定义好的BinderObj的asInterface方法返回一个代理对象,再调用方法进行交互
public class MainActivity extends AppCompatActivity {
private boolean isConnect = false;
private static final String TAG = "MainActivity";
private PersonManger personManger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start();
findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (personManger==null){
Log.e(TAG,"connect error");
return;
}
personManger.addPerson(new Person());
Log.e(TAG,personManger.getPersonList().size()+"");
}
});
}
private void start() {
Intent intent = new Intent(this, ServerSevice.class);
bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG,"connect success");
isConnect = true;
personManger = BinderObj.asInterface(service);
List<Person> personList = personManger.getPersonList();
Log.e(TAG,personList.size()+"");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG,"connect failed");
isConnect = false;
}
};
}
复制代码
这样的话,一次完成的进程间的交互就完成了~是否是感受没有想象中那么难,最后建议你们在不借助 AIDL 的状况下手写实现 Client 和 Server 进程的通讯,加深对 Binder 通讯过程的理解。
本文在写做过程当中参考了蛮多的文章和源码,感谢大佬们的无私奉献,溜了溜了~
参考文章