1,概述
首先,Binder是android平台下一种IPC机制,由于android基于linux,其linux本身就拥有一些ipc方式,如socket、通道等。但是,基于性能方面,android并未采用linux的ipc,而是自己实现了一套高效的ipc方式,Binder机制的特点,首先在于使用了内存映射技术,即linux下的mmap,使得client-service间通信只需要复制一次,而socket需要复制两次。Binder机制采用CS模式,为两个进程通信搭建桥梁。
说起mmap,不得不感叹Android对Binder的创新。
(1)如果基于共享内存进行IPC,效率虽然高,可是控制难啊。你就想一想Java内存模型吧,本身线程间共享一套内存,因此衍生处synchronize、volatile等等等等一系列锁或者锁升级、原子性啊、可见性啊一系列控制问题,如果Android还在进程间这么搞,有多复杂,你就想想吧!
(2)如果基于socket通信,那你也太慢了吧!网络传输是基于socket的tcp或者udp,没办法,网络传输的终端大概率不在同一终端,这个时候它们写入写出两次是理所当然的,可是Android呢?系统进程和用户进程就在同一手机嘛,你写入写出两次在应用层尚可,可是到Framework层就太慢了,毕竟系统服务慢,用户体验可不好。
那这这这?可这可咋办啊?
Google的大神们将方案1和2进行折中,想出了内存映射这个方案!你想想吧,如果共享内存不行,维护难度太高,那我们能不能让只有两个进程共享一块内存?话说到这,你就该明白了,就是将一大块Binder管理的内存,划分一部分给两个进程使用,这下就不存在多进程控制问题了是吧,这样的BpBinderh只需要写一次,BBinder不用去复制,直接到Binder给的一把钥匙,找到那块特定的内存区域去读取数据就可以了,这不就实现了两个进程间的通信嘛。当然哈,这也就产生一个弊端,分配给两个进程的共享内存不大,也就不能传输大数据了,当然在Framework层也没多大数据要传送,只是操作系统的一些参数,就很OK。
流程图:
简单时序图:
2,分析
日常开发中,如AIDL开发,需要继承Binder类,实现自己定义的服务接口,AIDL中有几个重点方法,如asInterface。 这在客户端上可以获取Proxy类,实现跨进程通信。当绑定一个服务后(另一个进程),返回的service本质是一个BinderProxy,对应native层BpBinder。通过特定描述符获取服务对象,实现跨进程调用。Proxy在客户端直接调用业务对应代理方法,其中mRemote可以理解为native层BpBinder,实际它们本身就建立了联系。创建代理需要服务端的IBinder在绑定服务时通过AMS的publishService交给客户端拿到(这本身又是个Binder通信),详细过程可查看bindService源码。
真正的通信过程,肯定要借助native层的BpBinder与Binder驱动进行通信,即通过内存映射的方式写入内存。前面说过,我们在AIDL中拿到的mRemote是服务端BinderProxy对象,然后调用asInterface方法生成代理类,其transact方法最终调用BinderProxy的transactNative方法,才能够将数据写进Binder驱动。
native层BpBinder直接与Binder驱动通信的类是IPCThreadState,在transact方法中代码如下,
来到IPCThreadState.transact方法,可以看到,Parcel数据报文已经传到这儿了,接下来将数据通过writeTransactionData方法写入,如何waitForResponse等待结果就行。
跟进writeTransactionData方法,通过mOut这个成员,就可以实现直接与Binder设备交互,
追根问底,mOut和对应的mIn是个啥呢?我们在IPCThreadState.h找到了答案,就是个Parcel。此时的mOut只是写入数据,还未与Binder驱动通信。
再看看waitForResponse,除了意料之中的mIn成员外,好家伙,talkWithDriver!
跟进talkWithDriver,发现底层使用ioctl方式与binder通信。ioctl是基于linux的一种io控制方式,第一个参数即Binder地址的文件描述法fd,第二个参数是控制协议,第三个参数与控制协议一同配合使用。
关于native层更深的实现暂时在此不展开分析,进程间通信已然可以实现,感兴趣的读者可以自学阅读android-s源码(写本文时google发布的最新版本)。
前面我们说过,BinderProxy对应native层BpBinder,如何对应?
这里,我们就来看看Binder构造方法,看,调用了一个Bnative方法,getNativeBBinderHolder,返回native层JavaBBinderHolder。
public Binder(@Nullable String descriptor) {
mObject = getNativeBBinderHolder();
//将自身和返回的指针值指向的JavaBBinderHolder建立关系
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Binder> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Binder class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mDescriptor = descriptor;
}
native层简单new了一个JavaBBinderHolder,它将native层Binder和Framework层Binder建立联系,虽然Framework层Binder是Native层镜像,但跨进程一定需要native层。JavaBBinderHolder所起的作用就是两层间传递者。
我们看下JavaBinderHolder.get方法,obj实际是Framework层Binder对象,new 了一个b,即JavaBBinder,保存在mBinder成员中,
那么,我们再看一看JavaBBinder的定义,
继承Binder,其中mObject成员指向Java层Binder对象,
那么到这,我们总结下,
Java层Binder.mObject ->(指向) native层JavaBBinderHolder
native层JavaBBinderHolder.mBinder -> native层JavaBBinder
native层JavaBBinder.-> Java层Binder
再看看JavaBBinder.onTransact方法,传入mObject对象即Java层Binder,反射调用execTransact方法,下一步不就来到服务端进程了嘛。所以,JavaBBinder起了一个传递作用。
在Java层Binder中,execTransact方法回调自身onTransact,进入服务端进程以实现对应业务啦,比如AMS的startActivityAsUser等,
private boolean execTransactInternal(int code, long dataObj, long replyObj, int flags,
...
if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0) {
AppOpsManager.startNotedAppOpsCollection(callingUid);
try {
res = onTransact(code, data, reply, flags);
} finally {
AppOpsManager.finishNotedAppOpsCollection();
}
} else {
// 回调服务端onTransact方法,进一步回调业务方法
res = onTransact(code, data, reply, flags);
}
} catch (RemoteException|RuntimeException e) {
...
}
...
return res;
}
客户端如何做?前面说过,客户端拿到的是BinderProxy,保存在mRemote成员中,调用BinderProxy.transact方法,跟进transactNative方法,跟进native层BpBinder将数据写入Binder驱动。
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
...
if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isListeningForOpNoted()) {
flags |= FLAG_COLLECT_NOTED_APP_OPS;
}
try {
// 进入底层实现,写入到BInder驱动,最后步骤是反射调用服务端execTransact方法
return transactNative(code, data, reply, flags);
} finally {
...
}
}
可能读者要问了,Binder.java不是本身就有transact方法吗?拿来干啥?
因为asInterface如果在同一进程中调用,是不用跨进程通信的,这个时候就不会返回Proxy对象。asInterface方法内先调用queryLocalInterface查询本进程中是否存在该Binder,如果存在直接返回,这个时候mRemote保存的就是本进程Binder对象而非BinderProxy,transact就被本进程调用了。
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
//调用这个方法的前提是同一进程
public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
3,应用层简单实例
通过AIDL自动生成以下代码,
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.zjw.source.aidl;
public interface IBookManager extends android.os.IInterface {
// 方法接口
public java.util.List<Book> getBookList() throws android.os.RemoteException;
public void addBook(Book book) throws android.os.RemoteException;
/**
* Default implementation for IBookManager.
* 这个接口默认实现,没啥用,当进程间无法获取Proxy时,返回一个空实现
*/
public static class Default implements IBookManager {
@Override
public java.util.List<Book> getBookList() throws android.os.RemoteException {
return null;
}
@Override
public void addBook(Book book) throws android.os.RemoteException {
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/**
* Local-side IPC implementation stub class.
* 这是具体实现的接口,本身是个Binder
*/
public static abstract class Stub extends android.os.Binder implements IBookManager {
//Binder唯一标识,一般用当前类名表示
private static final String DESCRIPTOR = "com.zjw.source.aidl.IBookManager";
// 自动生成的参数,表示方法
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.zjw.source.aidl.IBookManager interface,
* generating a proxy if needed.
*/
//用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,
//如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.Proxy对象。
public static IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
//这里先在本地进程查询,如果查询到说明在同一进程下
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBookManager))) {
return ((IBookManager) iin);
}
// 查询不到,返回代理对象
return new Proxy(obj);
}
//此方法返回Binder对象本身
@Override
public android.os.IBinder asBinder() {
return this;
}
//这个方法运行在服务端中的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装好后交由次方法来处理,
//服务端通过code可以确认客户端所请求的目标方法是什么,接着从data中取出目标方法所需要的参数,然后执行目标方法,
//当目标方法执行完毕后,就向reply中写入返回值(如果有返回值),
//如果此方法返回false,那么客户端的请求会失败。
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
//getBookList方法
case TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
java.util.List<Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
//addBook方法
case TRANSACTION_addBook: {
data.enforceInterface(descriptor);
Book _arg0;
if ((0 != data.readInt())) {
_arg0 = Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
//下面即是返回给客户端的Proxy对象
private static class Proxy implements IBookManager {
//服务端Binder
//服务端返回时: return new Proxy(obj);
private android.os.IBinder mRemote;
//接口默认实现
public static IBookManager sDefaultImpl;
Proxy(android.os.IBinder remote) {
//mRemote是个BinderProxy,可以去看一看Framework层源码
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
// 以下方法均是客户端调用
@Override
public java.util.List<Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<Book> _result;
try {
//这里先写入Binder Token 和 方法参数(如果有)
_data.writeInterfaceToken(DESCRIPTOR);
//调用transact发起远程请求,同时线程挂起
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_reply.readException();
_result = _reply.createTypedArrayList(Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBook(book);
return;
}
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
public static boolean setDefaultImpl(IBookManager impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static IBookManager getDefaultImpl() {
return Proxy.sDefaultImpl;
}
}
}
然后,服务端可以重写Stub方法了,如下所示:
public class MyService extends Service {
public static final String TAG = "MyService";
private final List<Book> mBookList = new ArrayList<>();
//服务端实现的方法
private final IBookManager.Stub mBookManager = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
synchronized (this) {
Log.d(TAG, "getBookList");
return mBookList;
}
}
@Override
public void addBook(Book book) throws RemoteException {
synchronized (this) {
Log.d(TAG, "addBook: " + book.toString());
mBookList.add(book);
}
}
@Override
public void removeLastBook() throws RemoteException {
synchronized (this) {
Log.d(TAG, "removeLastBook");
if (!mBookList.isEmpty()) mBookList.remove(mBookList.size() - 1);
}
}
};
@Override
public IBinder onBind(Intent intent) {
return mBookManager;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate: myPid->" + Process.myPid());
mBookList.add(new Book(0, "Android开发艺术探索"));
}
}
客户端调用
//自定义Service
private inner class MyServiceConnection : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mPing = true
mBookManager = IBookManager.Stub.asInterface(service)
service?.linkToDeath(MyDeathRecipient(),0)
Log.d(TAG, "onServiceConnected: ")
}
override fun onServiceDisconnected(name: ComponentName?) {
Log.d(TAG, "onServiceDisconnected: 连接断开,尝试重写连接")
mPing = false
}
}
private inner class MyDeathRecipient:IBinder.DeathRecipient{
override fun binderDied() {
Log.d(TAG, "binderDied: ")
mPing = false
mBookManager.asBinder().unlinkToDeath(this,0)
attemptConnectService()
}
}
// 某Activity
button3.setOnClickListener {
if (mBookManager.asBinder().pingBinder()) {
mBookManager.removeLastBook()
Log.d(TAG, mBookManager.bookList.toString())
} else {
Log.d(TAG, "onServiceDisconnected: 连接断开,尝试重写连接")
attemptConnectService()
}
}
button4.setOnClickListener {
if (mBookManager.asBinder().pingBinder()) {
mBookManager.addBook(Book(mBookManager.bookList.size, "add book from client"))
Log.d(TAG, mBookManager.bookList.toString())
} else {
Log.d(TAG, "onServiceDisconnected: 连接断开,尝试重写连接")
attemptConnectService()
}
}
/***
AndroidManifest.xml
<service
android:name=".service.MyService"
android:exported="true"
android:process=":remote" />
***/
日志
|