一、概述
因为进程间的资源是不能共享的,所以为了保证进程间能达到通信的目的,每个系统都存在自己的IPC(Inter-Process Communication)机制。Android系统中,涉及到进程间的通信底层都是依赖于Binder机制。
二、Binder
- IPC 原理
每个Android进程,只能运行在自己进程所运行的虚拟地址空间,这个虚拟地址空间包含用户空间和内核空间。对于用户空间,不同进程之间彼此不能共享,而内核之间是可以共享的。Client进程向Server进程通信,恰恰是利用进程间可共享的内核空间来完成底层通信工作的。
- Binder原理
Binder通信采用C/S架构,从组件层面来说,包含Client、Server、ServiceManager及Binder驱动,其中ServiceManager用于管理系统中的各种服务,架构图如下所示:
Binder通信的四个角色:
Client进程:使用服务的进程
Server进程:提供服务的进程
ServiceManager进程:ServiceManger的作用是将字符形式的Binder名字转化成Client中对该Binder的引用,使得Client能够通过Binder名字获得对Server中Binder实体的引用。
Binder驱动:驱动负责进程之间Binder通信的建立,Binder在进程之间的传递,Binder引用计数器管理,数据包在进程之间的传递和交互等一系列底层支持。
Binder运行机制:
注册服务:Server进程要先注册Service到ServiceManager,在该过程中Server是客户端,ServiceManager是服务端。
获取服务:Client进程使用某个Service之前,需先想ServiceManager中获取相应的Service。在该过程中Client是客户端,ServiceManager是服务端。
使用服务:Client根据得到的Service信息建立与Service所在的Server进程通信的通路,然后就可以直接与Service交互。在该过程中Client是客户端,Server是服务端。
图中的Client、Server、ServiceManager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都是通过Binder驱动进行交互的,从而实现IPC通信方式。其中Binder驱动位于内核空间,Binder驱动和ServiceManager可以看做是Android平台的基础架构,而Client和Server是Android的应用层,开发人员只需要自定义实现Client,Server端,借助Android平台基本架构便可以直接进行IPC通信。
既然Anrdoid进程间通信需要借助Android平台基本架构,那么Android就需要定制一些通信规则。具体就是:Android系统将一个可远程操作的应用定义为IBinder
,在这个接口中定义了可远程调用对象应该具有的属性和方法,在代码中实际使用的Binder也都是继承自IBinder对象。下面我们分析一下IBinder中定义的主要的属性和方法。
public interface IBinder {
//查看binder对应的进程是否存活
public boolean pingBinder();
//查看binder是否存活 需要注意的是,可能返回true的过程中,binder已经不可用了
public boolean isBinderAlive();
/**
* 执行一个对象的方法
*
* @param code 需要执行的命令. This should
* be a number between {@link #FIRST_CALL_TRANSACTION} and
* {@link #LAST_CALL_TRANSACTION}.
*
* @param data 传输的命令数据,一定不为空,如果不需要传任何数据,必须创建一个空的序列话对象
* @param reply 目标binder返回的处理结果,可能为null
*
* @param flags 操作方式, 0 等待 RPC 返回结果 , 1 单向的命令
*
* @return Returns the result from {@link Binder#onTransact}. A successful call
* generally returns true; false generally means the transaction code was not
* understood.
*/
public boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags)
throws RemoteException;
}
总结的来说,就是IBinder抽象了远程调用的接口,任何一个可远程调用的对象都应该实现这个接口。由于IBinder对象是一个高度抽象的接口,直接使用这个接口对于应用层的开发者而言学习成本太高,因此Android实现了Binder作为IBinder的抽象类,提供了一些默认的本地实现,当开发者需要自定义实现的时候,只需要继承Binder并重写Binder中的protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException
方法即可。
三、进程间通信AIDL的使用和解析
AIDL其实就是通过Binder来实现的,因为我们在定义好aidl文件后,studio就会帮我们生成相应的Binder类,所以我们可以通过查看studio生成的代码来了解Binder的工作原理。
首先在main目录下创建aidl文件夹,创建IMyAidlInterface.aidl
// IMyAidlInterface.aidl
package com.huli.hulitestapplication;
interface IMyAidlInterface {
/**
* 这个方法是默认生成的,告诉我们能用的基本的数据类型有哪些
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
String getName();
int multiply(int left, int right);
}
然后Rebuild project后,新建一个Service,里面创建Binder类继承IMyAidlInterface.Stub,在Service的onBind方法中返回自己的Binder类
public class MyAIDLService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new MyAIDLBinder();
}
class MyAIDLBinder extends IMyAidlInterface.Stub {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public String getName() throws RemoteException {
return "MyAIDLBinder";
}
@Override
public int multiply(int left, int right) throws RemoteException {
return left * right;
}
}
}
可以看到我们的MyAIDLBinder继承的是IMyAidlInterface.Stub,这个类就是我们新建完aidl文件,Rebuild project后系统帮我们生成好的Binder类
//studio帮我们生成了一个继承android.os.IInterface的IMyAidlInterface接口,
//所有在Binder中传输的接口都必须实现IInterface接口,
//IMyAidlInterface接口定义了我们在AIDL文件中定义的方法,
//然后还有个静态内部抽象类Stub
public interface IMyAidlInterface extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
* Stub继承了android.os.Binder 并实现了IMyAidlInterface接口
*/
public static abstract class Stub extends android.os.Binder implements com.huli.hulitestapplication.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.huli.hulitestapplication.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* 该方法用于将服务端的Binder对象转换为客户端所需要的接口对象,
* 该过程区分进程,如果进程相同,就返回服务端Stub对象本身,
* 否则就返回封装后的Stub.Proxy对象
*/
public static com.huli.hulitestapplication.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.huli.hulitestapplication.IMyAidlInterface))) {
return ((com.huli.hulitestapplication.IMyAidlInterface) iin);
}
return new com.huli.hulitestapplication.IMyAidlInterface.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 该方法是运行在服务端的Binder线程中的,当客户端发起远程请求后,在底层封装后会交给此方法来处理,通过code来区分客户端请求的方法
*/
@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_basicTypes: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0 != data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_getName: {
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_multiply: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.multiply(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/**
* 该代理类主要用于返回给客户端一个服务代理,跟服务本身的方法一一对应,当客户端发起请求时,_data就会写入参数,然后调用transact发起RPC请求,当前线程会被挂起,然后服务端的onTransact方法就会被调用,当RPC过程返回后,当前线程就会继续执行,并从_reply取出返回值(如果有的话),并返回
*/
private static class Proxy implements com.huli.hulitestapplication.IMyAidlInterface {
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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean) ? (1) : (0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.lang.String getName() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public int multiply(int left, int right) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(left);
_data.writeInt(right);
mRemote.transact(Stub.TRANSACTION_multiply, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_multiply = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public java.lang.String getName() throws android.os.RemoteException;
public com.huli.hulitestapplication.beans.Person getPerson() throws android.os.RemoteException;
public int multiply(int left, int right) throws android.os.RemoteException;
}
分析完studio帮我们生成的Binder之后,我们大概知道AIDL原理,定义好AIDL文件只是方便studio帮我们生成所需要的Binder类,AIDL并不是必须的文件,因为这个Binder类我们可以手写出来。所以最终要的还是Binder的知识点,其他一些IPC方式都是通过Binder来实现的,比如Messager,Bundle,ContentProvider,只是他们封装的方式不一样而已。
上面服务定义好之后,需要在服务端清单文件中注册
<service
android:name=".services.MyAIDLService"
android:exported="true">
<intent-filter>
<action android:name="com.huli.hulitestapplication.services.MyAIDLService" />
</intent-filter>
</service>
客户端使用
Intent bindIntent = new Intent();
//Android 5.0出来后,其中有个特性就是Service Intent must be explitict。本地调用使用显示调用解决,但是aidl跨进程访问service时候确实需要使用隐式调用。
bindIntent.setAction("com.huli.hulitestapplication.services.MyAIDLService");
//解决方法是设置package
bindIntent.setPackage("com.huli.hulitestapplication");
bindService(bindIntent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//这里其实拿到的是生成的Proxy对象
mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
ToastUtil.toastAbove(this, mIMyAidlInterface.getName());
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, BIND_AUTO_CREATE);
补充:上面aidl只用的基本数据类型,如果需要用自定义对象,需要以下步骤
1、 自定义对象(比如Person)需要实现Parcelable
2、 在aidl目录下创建跟Person.java所在包名一样的目录,创建Person.aidl放置在该目录下
3、 Person.aidl更改代码为
// Person.aidl 该类所在的包名需要跟Person.java所在的包名一致
package com.huli.hulitestapplication.beans;
parcelable Person;
4、 IMyAidlInterface就可以写方法Person getPerson();
,但是需要导入包import com.huli.hulitestapplication.beans.Person;
四、系统服务使用解析
我们通常跨进程调用系统服务部分代码如下:
//获取WindowManager服务引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
//布局参数layoutParams相关设置略...
View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null);
//添加view
wm.addView(view, layoutParams);
注册服务:在Android开机启动的过程中,Android会初始化各种系统的Service,并将这些Service向ServiceManager注册,这些都是系统自动完成的。
获取服务:客户端首先向ServiceManager查询得到具体的Service引用,通常是Service的代理对象,然后进行一些操作。
使用服务:通过这个引用向具体的服务端发送请求,服务端执行完成后就返回,即上面的第6行的WindowManager的addView函数,将触发远程调用,调用的是运行在SystemServer进程中的WindowManager的addView函数。
具体流程为:
- client通过ServiceManager获得一个Server的代理接口,对Server进行调用, 代理接口中定义的方法与server中定义的方法是一一对应的
- client调用某个代理接口中的方法时,代理接口的方法会将client传递的参数打包成Parcel对象
- 代理接口将Parcel发送给内核中的binder driver
- server会读取binder driver中的请求数据,如果是发送给自己的,解析Parcel对象,处理并将结果返回
- 整个的调用过程是一个同步过程,在server处理的时候,client会被block住
五、总结
从应用层来说,Binder是客户端和服务端之间通信的媒介。从Framework层来说,Binder是ServiceManager链接各种Manager和ManagerSevice的桥梁。所以Binder在Android中作为独有的IPC方式,如果我们能够更好的理解它,对我们的开发工作会非常有益。