概述
Binder是Android系统提供的一种IPC(进程间通信)机制,整个android应用层都是基于Binder系统之上的,它就像一个网络,把系统各个服务连接在了一起;当然Android中还有其它的IPC机制,例如Socket,PIC。
Binder是的通信是C/S架构体系,Server端管理着所有的Client请求;当然在这里还有一个重要的全局服务ServiceManager,它的作用是管理着系统中的各种服务。Server在开户的时候会注册到ServiceManager中去,当Client需要使用这些Service时会通过ServiceManager来拿到这些服务。三都的关系如下:
自定义的Binder通信
AIDL概述:
在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 显然, Java中是不支持跨进程内存共享的。因此要传递对象, 需要把对象解析成操作系统能够理解的数据格式, 以达到跨界对象访问的目的。在JavaEE中,采用RMI通过序列化传递对象。在Android中, 则采用AIDL(Android Interface Definition Language:接口定义语言)方式实现。
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC)。AIDL的IPC机制和EJB所采用的CORBA很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的。
自定义进程通信:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354551.在A应用中创建一个IDownLoad的aidl文件,然后编译,会在studio的build文件中生成IDownLoad的接口文件package com.example.lidaofu.demo;// Declare any non-default types here with import statementsinterface IDownLoad {void downLoad(String downUrl);}2.将build的IDownLoad文件复制到B应用中,创建一个服务;在服务的onBinder方法中返回了接口实现类public class DownService extends Service {public IBinder onBind(Intent intent) {return new IDownLoad.Stub() {//这里是远程端真正"工作"的地方public void downLoad(String downUrl) throws RemoteException {System.out.println("start down:" + downUrl);}};}}3.在A应用中开户服务并绑定服务public class MainActivity extends AppCompatActivity {protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//开户绑定服务Intent intent=new Intent(this,DownService.class);bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE);}private ServiceConnection serviceConnection=new ServiceConnection() {public void onServiceConnected(ComponentName name, IBinder service) {//在B应用是onBinder方法中返回的IBinder传递到这里,在这里通过asInterface方法//将IBinder转换成了我们需要的对象并调用,并调用它的方法IDownLoad downLoadService=IDownLoad.Stub.asInterface(service);try {downLoadService.downLoad("http://wwww.baidu.com");} catch (RemoteException e) {e.printStackTrace();}}public void onServiceDisconnected(ComponentName name) {}};}这样我们就完成了本地端与远程端的通信了,这里我们来看下编译生成的IDownLoad这个接口,这个接口是编译后系统生成,系统帮我们自动生成了Stub,Proxy二个静态内部类,Stub是Service端的代理类,Proxy是Client端的代理类,当我们要调用Service端的方法时不是直接能调用而是通过Client的代理类Proxy来访问Service端的Stub,Stub来调用服务端定义的方法,而这个方法就是我们在onBind方法中创建Stub时实现的方法;
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101public interface IDownLoad extends android.os.IInterface {/**Stub是个抽象方法,它是我们服务端的代理 ,当我们的Client端通过Proxy调用Service端时,它调用的其实是Stub的onTransact()方法,在这个方法内部根据方法法来判断我们调用的是那个方法,在Client中调用downLoad方法时,对应的它会通过code来调用this.downLoad(arg0)方法,而这个方法是未抽象的,它是我们在服务中创建Stub代理类时要实现的;*/public static abstract class Stub extends android.os.Binder implements com.example.lidaofu.demo.IDownLoad {//这个静态常量是用来区分Binder的,相当于Binder的身份标识private static final java.lang.String DESCRIPTOR = "com.example.lidaofu.demo.IDownLoad";//将这个Binder绑定身份标识public Stub() {this.attachInterface(this, DESCRIPTOR);}/*** 将IBinder对象转换成我们需要的Service,*/public static com.example.lidaofu.demo.IDownLoad asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}//根据Binder身份标识来查询这个Binder对象,我们知道如果Binder在同一个进程中//我们是可以根据身份标识查出这个对象的,如果对象不为空就证明Client调用方和//Service端在同一个进程,这样就不需要创建Proxy,直接返回这个Binder即可android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);//Client和Service在同一个进程if (((iin != null) && (iin instanceof com.example.lidaofu.demo.IDownLoad))) {return ((com.example.lidaofu.demo.IDownLoad) iin);}//Client和Service不在同一个进程,这时我们需要创建一个Client的代理类返回给//Client供Client端调用return new com.example.lidaofu.demo.IDownLoad.Stub.Proxy(obj);}public android.os.IBinder asBinder() {return this;}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_downLoad: {data.enforceInterface(DESCRIPTOR);java.lang.String _arg0;_arg0 = data.readString();this.downLoad(_arg0);reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}//这个是Client端的代理类,通过它来访问Service端提供的服务;我们看它的构造方法,这个//mRemote是怎么来的呢,我们看下它是怎么调用的呢,在我们开始绑定Service的时候我们看//到调用了IDownLoad.Stub.asInterface(service)这个方法,在asInterface()方法中我//看到当Client跟Service不在同一个进程时创建Proxy(IBinder remote)来返回给Client//端,这样我们可以看到这个mRemote其实就是我们的Service端private static class Proxy implements com.example.lidaofu.demo.IDownLoad {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}public android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}//当我们调用这个方法时,它内部调用的是mRemote.transact()这个方法,这个//方法调用的是父类的方法,具体看下面分析public void downLoad(java.lang.String downUrl) 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.writeString(downUrl);mRemote.transact(Stub.TRANSACTION_downLoad, _data, _reply, 0);_reply.readException();} finally {_reply.recycle();_data.recycle();}}}static final int TRANSACTION_downLoad = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}public void downLoad(java.lang.String downUrl) throws android.os.RemoteException;}我们看到Proxy最后调用的是mRmote.transact(int code, Parcel data, Parcel reply,int flags)这个方法,通过int类型的code来区分我们调用的方法,而reply就是序列化的我们要传递的参数了;看到这里我们惊奇的发现,它里面调用的竟然是onTransact(code, data, reply, flags),这个方法不是Stub类中有的嘛;对就是这样Proxy就是我们有Client端代表跟Server通信,而Server的回应就是Stub;onTransact(code, data, reply, flags)方法Binder也实现了,不过一般的Stub都会复写这个方法。
12345678910111213141516171819/Users/lidaofu/Library/Android/sdk/sources/android-25/android/os/Binder.java/*** Default implementation rewinds the parcels and calls onTransact. On* the remote side, transact calls into the binder to do the IPC.*/public final boolean transact(int code, Parcel data, 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;}下面我们来总结下Binder通信要点:
1.IDownLoad接口继承于android.os.IInterface;默认会生成二个静态的内部类Stub,Proxy;Client跟Server不是直接通信的;Client通过Proxy跟Server的Stub通信;Proxy是Client跟Server通信的代表,Stub是Server跟Client通信的代表;
2.Stub中有二个方法,asInterface(android.os.IBinder obj)表示将IBinder转换成接口,asBinder()表示将接口转换成IBinder;
3.Proxy中有个变量mRemote,表示的就是Server端的引用;当我们调用Server端的方法时最后调用的都是mRemote.transact(int code,Parcel data,Parcel reply,int flags)这个方法,这个方法将我们要调用的方法通过code变量来区分开来并最终调用了Server端的onTransact(code, data, reply, flags)方法,而onTransact方法中调用的就是我们Server端要实现的方法;系统中的Binder分析
了解了自定义的Binder的通信,我们知道有一个Stub和一个Proxy这样的东西来进行Client跟Server的通信;那么系统中的Client跟Server又是怎么通信的呢,其实原理是一样的,不管是系统还是自定义的
Binder,要想进行进程间的通信都是需要有一个Stub跟一个Proxy;下面我们来看下系统中的PackageManagerServer是如何进行工作的;
我们来看下IPackageManager.aidl
我们发现确实是有Stub跟Proxy这二个类,跟自定义Binder通信一样,它们也是通过这二个类来实现Binder的跨进程通信的;
看下我们的PackageManagerServer类1public class PackageManagerService extends IPackageManager.Stub发现它是继承于IPackageManager.Stub这个类查看代码发现它实现了IPackageManager接口中的方法那么就可以得知当Stub中通过onTransact调用的方法我们的PackageManagerService确实实现了,那么Client端是如何得到这个Server端的引用的呢?
通过查看源码,最终它会调用到这个方法,返回PackageManagerServer服务的引用;1234567891011public static IPackageManager getPackageManager() {if (sPackageManager != null) {//Slog.v("PackageManager", "returning cur default = " + sPackageManager);return sPackageManager;}IBinder b = ServiceManager.getService("package");//Slog.v("PackageManager", "default service binder = " + b);sPackageManager = IPackageManager.Stub.asInterface(b);//Slog.v("PackageManager", "default service = " + sPackageManager);return sPackageManager;}通过这里我们看到ServiceManager.getService(“package”)这个方法,ServiceManager又是什么东西呢?其实它是我们系统服务的总管,它管理着系统是的所有Server服务,当我们开机时它就会启动并且将系统中一些重要的Server启动且将Server的引用保存起来,当我们需要这些Server时它就会通过getService()将服务返回给我们,以供我们使用;在下一章我们再详细分析这个ServiceManager;这样我们就可以理解我们的这个PackageManager的引用是怎么来的呢。跟自定义的Binder比起来,发现自定义的Server是通过我们绑定服务时的onBinder返回给Client的,而系统的是直接在总管ServerManager那拿到的。
Binder分析
坚持原创技术分享,您的支持将鼓励我继续创作!