Java层Binder机制详解

本文是我阅读《Android内核剖析》一书的笔记,在此写下来是希望能够加深理解,也希望朋友们能够指出其中的不足。https://i-blog.csdnimg.cn/blog_migrate/1b742e3d4e6a6515827cb7b1388537e5.png

Binder是一种基于C/S的架构,主要包括四个部分:服务端(Server),客户端(Client),Binder驱动,ServiceManager。Binder是Android系统中非常重要的一种IPC机制,如果你想研究Frameworks,必须先对Binder机制诱有一定的认识,否则是无法看懂Frameworks源码的。上述四个部分分别在不同的进程中,他们之间的交互都是通过Binder实现,现在站在宏观的角度来分析Server,Client,ServiceManager之间的关系,如下图:

                   

分析上图:

(1)    当一个服务进程启动后,需要将其中的Service注册到ServiceManager中,此时底层会有两个变化:①后台启动一个监听线程,监听Binder驱动发过来的消息 ② 在Binder驱动中生存一个Service的Binder引用

(2)    Client想和某一个Service交互,需要向ServiceManager申请获得该Service在Binder中的引用,并存放在自身的mRemote变量中,之后通过mRemote调用。

上图展示了客户端如何和服务端交互:

 客户端获取服务端在Binder驱动中的应用,这个引用改写了transact方法(这个方法来自于IBinder),这个方法通过调用onTransact方法实现如下功能:首先向服务端发送一个消息调用,并挂起当前线程,然后等待服务端执行完毕返回,最后继续执行客户端线程。

下面使用AIDL工具生成一个简单的服务端和客户端吧。在一个包中新建一个ImusicPlayerService.aidl文件,这时在R.java目录下回自动生成一个ImusicPlayerService.java文件。

ImuiscPlayerService.aidl

[java]  view plain  copy
 print ?
  1. interface IMusicPlayerService  
  2. {  
  3.     void start(String path);  
  4.     void stop();  
  5. }  

ImusicPlayerService.java文件

[java]  view plain  copy
 print ?
  1. public interface IMusicPlayerService extends android.os.IInterface  
  2. {  
  3. /** Local-side IPC implementation stub class. */  
  4. public static abstract class Stub extends android.os.Binder implements com.binder.demo.IMusicPlayerService  
  5. {  
  6.     private static final java.lang.String DESCRIPTOR = "com.binder.demo.IMusicPlayerService";  
  7. /** Construct the stub at attach it to the interface. */  
  8.     public Stub()  
  9.     {  
  10.         this.attachInterface(this, DESCRIPTOR);  
  11.     }  
  12. /** 
  13.  * Cast an IBinder object into an com.binder.demo.IMusicPlayerService interface, 
  14.  * generating a proxy if needed. 
  15.  */  
  16.     public static com.binder.demo.IMusicPlayerService asInterface(android.os.IBinder obj)  
  17.     {  
  18.         if ((obj==null)) {  
  19.             return null;  
  20.         }  
  21.         android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
  22.         if (((iin!=null)&&(iin instanceof com.binder.demo.IMusicPlayerService))) {  
  23.             return ((com.binder.demo.IMusicPlayerService)iin);  
  24.         }  
  25.         return new com.binder.demo.IMusicPlayerService.Stub.Proxy(obj);  
  26.     }  
  27.     @Override   
  28.     public android.os.IBinder asBinder()  
  29.     {  
  30.     return this;  
  31.     }  
  32.     @Override   
  33.     public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException  
  34.     {  
  35.         switch (code)  
  36.         {  
  37.             case INTERFACE_TRANSACTION:  
  38.             {  
  39.                 reply.writeString(DESCRIPTOR);  
  40.                 return true;  
  41.             }  
  42.             case TRANSACTION_start:  
  43.             {  
  44.                 data.enforceInterface(DESCRIPTOR);  
  45.                 java.lang.String _arg0;  
  46.                 _arg0 = data.readString();  
  47.                 boolean _result = this.start(_arg0);  
  48.                 reply.writeNoException();  
  49.                 reply.writeInt(((_result)?(1):(0)));  
  50.                 return true;  
  51.             }  
  52.             case TRANSACTION_stop:  
  53.             {  
  54.                 data.enforceInterface(DESCRIPTOR);  
  55.                 this.stop();  
  56.                 reply.writeNoException();  
  57.                 return true;  
  58.             }  
  59.         }  
  60.         return super.onTransact(code, data, reply, flags);  
  61.     }  
  62.     private static class Proxy implements com.binder.demo.IMusicPlayerService  
  63.     {  
  64.         private android.os.IBinder mRemote;  
  65.         Proxy(android.os.IBinder remote)  
  66.         {  
  67.         mRemote = remote;  
  68.         }  
  69.         @Override   
  70.         public android.os.IBinder asBinder()  
  71.         {  
  72.             return mRemote;  
  73.         }  
  74.         public java.lang.String getInterfaceDescriptor()  
  75.         {  
  76.             return DESCRIPTOR;  
  77.         }  
  78.         @Override   
  79.         public boolean start(java.lang.String filePath) throws android.os.RemoteException  
  80.         {  
  81.             android.os.Parcel _data = android.os.Parcel.obtain();  
  82.             android.os.Parcel _reply = android.os.Parcel.obtain();  
  83.             boolean _result;  
  84.             try {  
  85.                 _data.writeInterfaceToken(DESCRIPTOR);  
  86.                 _data.writeString(filePath);  
  87.                 mRemote.transact(Stub.TRANSACTION_start, _data, _reply, 0);  
  88.                 _reply.readException();  
  89.                 _result = (0!=_reply.readInt());  
  90.             }  
  91.             finally {  
  92.                 _reply.recycle();  
  93.                 _data.recycle();  
  94.             }  
  95.             return _result;  
  96.         }  
  97.         @Override   
  98.         public void stop() throws android.os.RemoteException  
  99.         {  
  100.             android.os.Parcel _data = android.os.Parcel.obtain();  
  101.             android.os.Parcel _reply = android.os.Parcel.obtain();  
  102.             try {  
  103.                 _data.writeInterfaceToken(DESCRIPTOR);  
  104.                 mRemote.transact(Stub.TRANSACTION_stop, _data, _reply, 0);  
  105.                 _reply.readException();  
  106.             }  
  107.             finally {  
  108.                 _reply.recycle();  
  109.                 _data.recycle();  
  110.             }  
  111.         }  
  112.     }  
  113.     static final int TRANSACTION_start = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);  
  114.     static final int TRANSACTION_stop = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);  
  115. }  
  116. public boolean start(java.lang.String filePath) throws android.os.RemoteException;  
  117. public void stop() throws android.os.RemoteException;  
  118. }  

我们来重点分析一下两个重要的类:

class Stub extends android.os.Binderimplements com.binder.demo.ImusicPlayerService

private static class Proxy implementscom.binder.demo.ImusicPlayerService


对照上面一个图很容易知道Stub这个类对应的是Server端,而Proxy对应的是客户端,那么首先从客户端分析:


在客户端中,我们看到一个私有变量mRemote,它存放的就是服务端在Binder驱动中的一个引用。接下来分析start方法:为了查看方便,我再次将start方法代码贴出来:

[java]  view plain  copy
 print ?
  1. public boolean start(java.lang.String filePath) throws android.os.RemoteException  
  2.     {  
  3.             android.os.Parcel _data = android.os.Parcel.obtain();  
  4.             android.os.Parcel _reply = android.os.Parcel.obtain();  
  5.             boolean _result;  
  6.             try {  
  7.                 _data.writeInterfaceToken(DESCRIPTOR);  
  8.                 _data.writeString(filePath);  
  9.                 mRemote.transact(Stub.TRANSACTION_start, _data, _reply, 0);  
  10.                 _reply.readException();  
  11.                 _result = (0!=_reply.readInt());  
  12.             }  
  13.             finally {  
  14.                 _reply.recycle();  
  15.                 _data.recycle();  
  16.             }  
  17.             return _result;  
  18.         }  

发现start里面其实就是调用的mRemote中的transact方法,这不就是一种典型的代理模式吗?我想这里之所以使用代理模式,是为了在将参数打包发送到服务端去之前,固定各个参数在包中的顺序,方便服务端去除数据。至于其中的Stub.TRANSACTION_start是在Stub类中定义的一个变量,用来标示函数的。

static final int TRANSACTION_start =(android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

 

transact函数经改写后,本质就是调用了Stub中的onTransact函数,那么我们来分析一下服务端的onTransact函数吧。同样为了方便,再次贴出:

[java]  view plain  copy
 print ?
  1. public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException  
  2.     {  
  3.         switch (code)  
  4.         {  
  5.             case INTERFACE_TRANSACTION:  
  6.             {  
  7.                 reply.writeString(DESCRIPTOR);  
  8.                 return true;  
  9.             }  
  10.             case TRANSACTION_start:  
  11.             {  
  12.                 data.enforceInterface(DESCRIPTOR);  
  13.                 java.lang.String _arg0;  
  14.                 _arg0 = data.readString();  
  15.                 boolean _result = this.start(_arg0);  
  16.                 reply.writeNoException();  
  17.                 reply.writeInt(((_result)?(1):(0)));  
  18.                 return true;  
  19.             }  

由于传入的code是TRANSACTION_start,所以服务端首先从包中按照客户端的写入顺序读出数据,然后调用自己的start方法,也就是说我们已经完成在客户端调用服务端的方法了,然后将返回数据写入到reply包中,Binder驱动接到这个通知后,就会继续执行客户端线程从这个包中去除数据。

 

细心的朋友可能会发现一个问题,还有一个非常关键的问题没有解决,客户端是如何拿到服务端的Binder应用的,下面就来分析一下这个问题

 

在写程序的时候,经常会使用getSystemService这个函数来获取一个XXXManager,例如我需要获得一个InputMethodManager,那么我会这么做:

[java]  view plain  copy
 print ?
  1. <pre name="code" class="java">InputMethodManagerwm=(InputMethodManager)context.getSystemService(“input_method”);  
  2. getSystemService这个函数是在ContextImpl中实现的,部分源码如下:  
  3. else if (INPUT_METHOD_SERVICE.equals(name)) {  
  4.             return InputMethodManager.getInstance(this);  
  5. 进入到getInstance(this)看看源码:  
  6. static public InputMethodManager getInstance(Looper mainLooper) {  
  7.         synchronized (mInstanceSync) {  
  8.             if (mInstance != null) {  
  9.                 return mInstance;  
  10.             }  
  11.             IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);  
  12.             IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);  
  13.             mInstance = new InputMethodManager(service, mainLooper);  
  14.         }  
  15.         return mInstance;  
  16. }  
  17. </pre><br><br>  

代码中通过使用ServiceManager中的getService方法获取到一个Binder对象,然后通过这个Binder对象new 一个InputMethodManager出来并返回,这里我们不得不猜想这个Binder对象就是一个服务端的引用,为了验证猜想,继续进入到getService看看

[java]  view plain  copy
 print ?
  1. public static IBinder getService(String name) {  
  2.         try {  
  3.             IBinder service = sCache.get(name);  
  4.             if (service != null) {  
  5.                 return service;  
  6.             } else {  
  7.                 return getIServiceManager().getService(name);  
  8.             }  
  9.         } catch (RemoteException e) {  
  10.             Log.e(TAG, "error in getService", e);  
  11.         }  
  12.         return null;  
  13.     }  

还挺复杂的,我们进入getIServiceManager中看看吧,估计第一次学习Binder的同学已经晕了,没事多看几遍就很清晰的。

[java]  view plain  copy
 print ?
  1. private static IServiceManager getIServiceManager() {  
  2.         if (sServiceManager != null) {  
  3.             return sServiceManager;  
  4.         }  
  5.   
  6.         // Find the service manager  
  7.         sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());  
  8.         return sServiceManager;  
  9. }  

到了这里就涉及到几点知识点了,容我慢慢道来:

(1)    ServiceManager本身就是一个Service,那么我们的客户端再和它交互之前也必须获取到它在Binder中的引用,说道这里可能有些朋友就会想,我们不是要通过ServiceManager获取Binder应用吗,那么他自己的Binder应用怎么获取呢,呵呵,这个问题解决起来很简单,Android中提供了BinderInternal.getContextObject()函数返回ServiceManger的引用,说白了就是这个意思:ServiceManager的Binder引用通过上面这个函数获得,而其他Service的Binder引用需要通过ServiceManager获得。

(2)    这里有一个asInterfaec函数,它的代码大家可以查看Stub类中的asInterface,在这里我只说明一下它的作用。前面已经说过,一个Service说到底就是一个Binder对象,一个Binder对象创建后,就会在Binder驱动中形成一个引用,客户端就是通过这个引用和服务端交互,但是如果在这个Service进程内部调用这个Service,那么就没有必要使用IPC,直接可以使用这个Service对象(Binder对象),也就是说这个函数就是对外界提供了统一的接口,不管是远程调用还是本地调用,使用的都是同样的代码,

 

到这里我们会发现,在XXXManager里面有一个IXXX类型的变量,如果是远程调用,那么就是Proxy累,如果是本地调用,存放的就是Stub类


总结:

(1)      Server所在进程启动后,需要将其中的Servic注册到ServiceManager,这样Client就可以通过ServiceManager找到对应的Binder引用,而ServiceManager自身的Binder引用时通过BinterInternal.getContextObject()函数获得

(2)      我们在写程序时,通过getSystemService获得XXXManager,在XXXManager中保存了一个IXXX变量,这个变量可能是Proxy类型,也可能是Stub类型,这个过程被asInterface函数屏蔽了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值