** 简单说一下,第一次写文章,有点不习惯,从下定决心看一看Android的系统源码开始,看过了简单的系统源码如何修改编译,简单了解了点Linux内核驱动的一点点知识,随后跟着老罗的Android系统源代码情景分析一书看了看Android的启动流程以及Activity跳转,因为这些知识点都须要对Binder机制的进行了解,所以决定看一看Binder机制,本文从Service跨进程间访问的角度了解,因为IBinder.Stub使用代理模式,所以本文也对代理模式简单阐述。(注:如有什么地方阐述有误,敬请指正。)html
代理模式的定义:代理模式属于结构型模式,指的是为其余对象提供一种代理以控制对这个对象的访问。在某些状况下,一个对象不适合或者不能直接引用另外一个对象,而代理对象能够在客户端和目标对象之间起到中介的做用。java
应用场景:当Client为了实现Subject的目标而直接访问RealSubject存在问题的时候(好比对象建立开销很大,或者某些操做须要安全控制,或者须要进程外的访问),就须要Proxy来代替Subject来执行request操做。例如:AOP切面编程 AIDL跨进程间通信等android
代理模式的主要角色以下:c++
类图以下: 编程
代码示例:安全
/**
* 代理接口
*/
public interface ProxyInterface {
public abstract void handlingEvents();// 处理事件
}
/**
* 真实类
*/
public class RealClass implements ProxyInterface {
@Override
public void handlingEvents() {
System.out.println("正在处理事件中......");
}
}
/**
* 代理类
*/
public class ProxyClass implements ProxyInterface {
private ProxyInterface real;
public Pursuit(ProxyInterface real) {
this.real = real;
}
@Override
public void handlingEvents() {
// todo 执行真实类方法以前能够在此作处理
real.handlingEvents();
// todo 执行真实类方法以后能够在此作处理
}
}
/**
* 调用
*/
public static void main(String[] args) {
RealClass real = new RealClass();
ProxyClass daili = new ProxyClass(real);
daili.handlingEvents();
}
复制代码
代码说明:bash
上述代码示例中,代理类调用真实类的同名方法,此时能够对真实类中的方法执行先后进行处理,好比:计算真实类中方法执行时间或者在真实类方法执行先后分别打印Log等等...,而在AIDL生成的Stub类中的代理类中,就在跨进程间方法执行先后分别调用了写入和读取驱动操做,此话题后续再详细分析。代理类的好处:代理类实现和真实类同样的接口,所以不一样代理类实现同一接口的代理类之间,也能够相互代理,下面用代码说明一下:服务器
/**
* 代理类1
*/
public class ProxyClass1 implements ProxyInterface {
private ProxyInterface real;
public Pursuit(ProxyInterface real) {
this.real = real;
}
@Override
public void handlingEvents() {
System.out.println("方法前");
real.handlingEvents();
System.out.println("方法后");
}
}
/**
* 代理类2
*/
public class ProxyClass2 implements ProxyInterface {
private ProxyInterface real;
public Pursuit(ProxyInterface real) {
this.real = real;
}
@Override
public void handlingEvents() {
long startTime=System.currentTimeMillis();
real.handlingEvents();
long endTime=System.currentTimeMillis();
float excTime=(float)(endTime-startTime)/1000;
System.out.println("执行时间:"+excTime+"s");
}
}
/**
* 调用
*/
public static void main(String[] args) {
RealClass real = new RealClass();
ProxyClass1 daili1 = new ProxyClass1(real);
ProxyClass2 daili2 = new ProxyClass2(daili1);
daili2.handlingEvents();
}
复制代码
上述示例代码中,只是用计时以及打印来表示代理模式的用法以及部分场景,固然场景不止这些,先放出一部分AIDL动态生成的Stub类的代理类中的部分代码:架构
/**
首先就是建立了3个对象_data 输入对象,_reply输出对象,_result返回值对象而后把参数信息 写入到_data里,
接着就调用了transact这个方法 来发送rpc请求,而后接着当前线程挂起, 服务端的onTransace方法才被调用,
调用结束之后 当前线程继续执行,直到从_reply中取出rpc的返回结果 而后返回_reply的数据
注:有返回值的方法才有_result对象
*/
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.administrator.aidlmessagetest.Person> _result;
try {
// 执行方法前
_data.writeInterfaceToken(DESCRIPTOR);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
// 真实调用跨进程间的方法
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
// 执行方法后
_reply.readException();
_result = _reply.createTypedArrayList(com.example.administrator.aidlmessagetest.Person.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
复制代码
动态代理:框架
如今要生成某一个对象的代理对象,这个代理对象一般也要编写一个类来生成,因此首先要编写用于生成代理对象的类。在java中如何用程序去生成一个对象的代理对象呢,java在JDK1.5以后提供了一个"java.lang.reflect.Proxy"类,经过"Proxy"类提供的一个newProxyInstance方法用来建立一个对象的代理对象。
Proxy类中的方法:(最经常使用的方法就是:newProxyInstance)
方法 1: 该方法用于获取指定代理对象所关联的InvocationHandler
static InvocationHandler getInvocationHandler(Object proxy)
复制代码
方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
static Class getProxyClass(ClassLoader loader, Class[] interfaces)
复制代码
方法 3:该方法用于判断指定类是不是一个动态代理类
static boolean isProxyClass(Class cl)
复制代码
方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
复制代码
JDK中的动态代理是经过反射类Proxy以及InvocationHandler回调接口实现的;可是,JDK中所要进行动态代理的类必需要实现一个接口,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具备必定的局限性,并且使用反射的效率也并非很高。
JDK动态代理的方案:
Bytecode Proxy:直接生成二进制字节码格式的代理类
动态代理方案性能对比:
(这也是为何网上有人说JAVAASSIST比JDK还慢的缘由,用JAVAASSIST最好别用它提供的动态代理接口,而能够考虑用它的字节码生成方式)
差别缘由: 各方案生成的字节码不同,像JDK和CGLIB都考虑了不少因素,以及继承或包装了本身的一些类, 因此生成的字节码很是大,而咱们不少时候用不上这些,而手工生成的字节码很是小,因此速度快。
哈哈哈,盗张图片清醒一下
接下来从新进入JDK提供的动态代理方式:Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler),这个方法有3个参数,下面分别来看一看:
public class TestInvacationHandler implements InvocationHandler {
// 被代理的真实对象
private final ProxyInterface object;
public TestInvacationHandler(ProxyInterface object){
this.object = object;
}
/**
* proxy:就是代理对象,newProxyInstance方法的返回对象
* proxy的做用:
* 1. 可使用反射获取代理对象的信息(也就是proxy.getClass().getName())。
* 2. 能够将代理对象返回以进行连续调用,这就是proxy存在的目的。由于this并非代理对象,
* method:调用的方法
* args: 方法中的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 方法执行前
System.out.println("---------before-------");
// 调用方法
Object invoke = method.invoke(object, args);
// Object invoke = method.invoke(proxy, args); // 若此处使用此句代码,则该程序会造成死循环
// 方法执行后
System.out.println("---------after-------");
return invoke;
}
}
复制代码
结合上面的ProxyInterface RealClass以及TestInvacationHandler完整调用:
public static void main(String[] args) {
ProxyInterface real = new RealClass();
ProxyInterface proInterface = (ProxyInterface)Proxy
.newProxyInstance(real.getClass().getClassLoader()
,RealClass.class.getInterfaces(), new TestInvacationHandler(real));
proInterface.handlingEvents();
}
复制代码
说到这里,其实就会引发一个颇有意思的问题,咱们为何要使用动态代理?
答:从动态代理的完整调用能够看出,动态代理使咱们免于去重写接口中的方法,
而着重于去扩展相应的功能或是方法的加强,与静态代理相比简单了很多,能够减小项目中的业务量
复制代码
至此,礼敬,代理模式到此已结束,如有什么地方阐述有误,敬请指正。
此图为Service跨进程间通信绑定服务时序图,Service绑定流程中也牵扯着IBinder调用,好比IActivityManager(ActivityManagerService)。
Binder机制基本了解:
进程隔离:出于安全考虑,一个进程不能操做另外一个进程的数据,进而一个操做系统必须具有进程隔离这个特性。在Linux系统中,虚拟内存机制为每一个进程分配了线性连续的内存空间,操做系统将这种虚拟内存空间映射到物理内存空间,每一个进程有本身的虚拟内存空间,进而不能操做其余进程的内存空间,每一个进程只能操做本身的虚拟内存空间,只有操做系统才有权限操做物理内存空间。进程隔离保证了每一个进程的内存安全,可是在大多数情形下,不一样进程间的数据通信是不可避免的,所以操做系统必须提供跨进程通讯机制。 进程空间分为内核空间和用户空间,内核空间(Kernel)是系统内核运行的空间。用户空间(User Space)是用户程序运行的空间,他们之间是隔离的。内核有访问的全部权限,用户空间也能够经过系统接口去访问内核空间。用户空间能够经过内核空间(相似于中介者)进行相互访问。
Binder机制优势:
传输性能好:
稳定性:
安全性高:
Binder机制实现原理图:(借图,下面会注明来处)
Binder机制之AIDL:
接下来根据生成的aidl文件源码分析跨进程间通信(aidl生成:new->AIDL->AIDL File-> Build) 原谅我又把别人写好的借来(下面会注明来处)
/**
标准的代理模式,代理类为Proxy,此文件为动态生成,所以Stub Proxy都为固定类名
*/
package com.example.administrator.aidlmessagetest;
//从前面几行就能看出来 生成的代码是一个 interface ,只不过这个interface是 android.os.IInterface 的子类!
public interface IPersonManager extends android.os.IInterface {
//这个接口里 有一个静态的抽象类Stub
//这个Stub是Binder的子类,而且实现了IPersonManager 这个接口
public static abstract class Stub extends android.os.Binder implements com.example.administrator.aidlmessagetest.IPersonManager {
//这个东西就是惟一的binder标示 能够看到就是IPersonManager的全路径名
private static final java.lang.String DESCRIPTOR = "com.example.administrator.aidlmessagetest.IPersonManager";
/**
* 这个就是Stub的构造方法,咱们若是写好aidl文件之后 写的service里面 是怎么写的?
* private final IPersonManager.Stub mBinder = new IPersonManager.Stub() {}
* 咱们都是这么写的 对吧~~因此想一想咱们的service里面的代码 就能辅助理解 这里的代码了
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
//这个方法 其实就作了一件事,若是是同一个进程,那么就返回Stub对象自己
//若是不是同一个进程,就返回Stub.Proxy这个代理对象了
public static com.example.administrator.aidlmessagetest.IPersonManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//若是是同1个进程,也就是说进程内通讯的话 咱们就返回括号内里的对象
if (((iin != null) && (iin instanceof com.example.administrator.aidlmessagetest.IPersonManager))) {
return ((com.example.administrator.aidlmessagetest.IPersonManager) iin);
}
//若是不是同一进程,是2个进程之间相互通讯,那咱们就得返回这个Stub.Proxy 看上去叫Stub 代理的对象了
return new com.example.administrator.aidlmessagetest.IPersonManager.Stub.Proxy(obj);
}
//返回当前对象
@Override
public android.os.IBinder asBinder() {
return this;
}
/**
* 只有在跨进程通讯的时候 才会调用这个方法 ,同一个进程是不会调用的。
*
* 首先 咱们要明白 这个方法 通常状况下都是返回true的,
* 也只有返回true的时候才有意义,若是返回false了就表明这个方法执行失败
* 因此咱们一般是用这个方法来作权限认证的,其实也很好理解,既然是多进程通讯,
* 那么咱们服务端的进程固然不但愿谁都能过来调用因此权限认证是必须的,权限认证先略过
*
* 除此以外 ,onTransact 这个方法就是运行在Binder线程池中的,
* 通常就是客户端发起请求,而后android底层代码把这个客户端发起的请求封装成3个参数,
* 来调用这个onTransact方法,第一个参数code就表明客户端想要调用服务端方法的标志位。
* 其实也很好理解 服务端可能有n个方法 每一个方法都有一个对应的int值来表明,
* 这个code就是这个int值,用来标示客户端想调用的服务端的方法
* data就是方法参数,reply就是方法返回值。都很好理解
*
* 其实隐藏了很重要的一点,这个方法既然是运行在binder线程池中的,
* 因此在这个方法里面调用的服务器方法也是运行在Binder线程池中的,
* 因此咱们要记得 若是你的服务端程序有可能和多个客户端相联的话,
* 你方法里使用的那些参数 必需要是支持异步的,不然的话值就会错乱了!
* 这点必定要记住!结论就是Binder方法 必定要是同步方法!!!!!!
*/
@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_getPersonList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.administrator.aidlmessagetest.Person> _result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addPerson: {
data.enforceInterface(DESCRIPTOR);
com.example.administrator.aidlmessagetest.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.administrator.aidlmessagetest.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
// 只有在跨进程通讯的状况下 才会返回这个代理的对象
private static class Proxy implements com.example.administrator.aidlmessagetest.IPersonManager {
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;
}
/**
* 这里有2个方法 一个getPersonList 一个addPerson,咱们就分析一个方法就能够了
*
* 首先就是建立了3个对象,
* _data 输入对象,_reply输出对象,_result返回值对象,把参数信息写入到_data里,
* 接着就调用了transact这个方法来发送rpc请求,而后接着当前线程挂起,
* 服务端的onTransace方法才被调用,调用结束*之后当前线程继续执行,
* 直到从_reply中取出rpc的返回结果真后返回_reply的数据因此这里咱们就要注意了,
* 客户端发起调用远程请求时,当前客*户端的线程就会被挂起了,
* 因此若是一个远程方法 很耗时,
* 咱们客户端就必定不能在ui main线程里在发起这个rpc请求,否则就anr了。
*
* 注:这2个方法运行在客户端!!!!!!!!!!!!!!!!
* 注:若方法无返回值,则无_result返回值对象
*/
@Override
public java.util.List<com.example.administrator.aidlmessagetest.Person> getPersonList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.administrator.aidlmessagetest.Person> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.administrator.aidlmessagetest.Person.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addPerson(com.example.administrator.aidlmessagetest.Person person) 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 ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.example.administrator.aidlmessagetest.Person> getPersonList() throws android.os.RemoteException;
public void addPerson(com.example.administrator.aidlmessagetest.Person person) throws android.os.RemoteException;
}
复制代码
bindService时序图中绑定服务操做是使用IBinder进行的,那么IBinder是如何获取的,何时初始化好的?从上述代码分析来看,只能从Stub()的构造方法入手,Stub构造方法一调用其父类构造方法顺便也会被调用,其父类构造方法代码以下:
public Binder() {
// private static native long getNativeBBinderHolder();
// getNativeBBinderHolder 为native方法
mObject = getNativeBBinderHolder();
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());
}
}
}
复制代码
从Binder构造方法能够看出,只要Stub被初始化就会调用Binder构造方法,从而会调用native层c/c++代码初始化一个IBinder,并从内核Binder驱动中进行注册。
继续看上述aidl生成的代码,要明确的是Proxy代理类是在客户端(即调用端),因此里面的方法也都是客户端调用,而onTransact方法则是在服务端(即目标端,跨进程访问端)。其方法调用过程就是:Proxy类方法(即客户端)调用mRemote.transact(),会触发目标端执行Binder::onTransact()(即上述代码中的onTransact方法),咱们能够看看transact()方法和onTransact()方法的参数对比:
/*
参数一:用来标识指令,即调用的什么方法。须要客户端和服务端约定好code码。
参数二:来自发送端的数据包。包含参数信息
参数三:来自发送端的接收包,往这个包中写数据,就至关于给发送端返回数据。
参数四:特殊操做标识。
*/
public boolean transact(int code, Parcel data, Parcel reply, int flags){}
public boolean onTransact(int code, Parcel data, Parcel reply, int flags){}
复制代码
能够看出来,两个是成对的操做。mRemote.transact()操做是一个阻塞式的操做,就是说在这个方法执行返回成功后,直接从reply中读取的数据就是远程端在Binder::onTransact()中填充的数据。而编译器自动帮咱们生成的onTransact()中,会读取data中数据,而后调用对应的方法。大体调用状况以下图:
至此,完毕。
最后盗图,哈哈
本文参考博客:
想研究更底层Binder机制可参考:www.jianshu.com/p/fe816777f…
敬请指教