美文网首页
Binder 例子

Binder 例子

作者: 黑色海鸥 | 来源:发表于2018-08-07 18:31 被阅读56次
public class Binder implement IBinder{
        void attachInterface(IInterface plus, String descriptor)
        IInterface queryLocalInterface(String descriptor) //从IBinder中继承而来
        boolean onTransact(int code, Parcel data, Parcel reply, int flags)//暂时不用管,后面会讲。
        ......
        final class BinderProxy implements IBinder {
        ......//Binder的一个内部类,暂时不用管,后面会讲。
        }
}
  • Binder具有被跨进程传输的能力是因为它实现了IBinder接口。系统会为每个实现了该接口的对象提供跨进程传输,这是系统给我们的一个很大的福利。
  • Binder具有的完成特定任务的能力是通过它的attachInterface方法获得的,我们可以简单理解为该方法会将(descriptor,plus)作为(key,value)对存入Binder对象中的一个Map<String,IInterface>对象中,Binder对象可通过attachInterface方法持有一个IInterface对象(即plus)的引用,并依靠它获得完成特定任务的能力。
  • queryLocalInterface方法可以认为是根据key值(即参数 descriptor)查找相应的IInterface对象。onTransact方法暂时不用管,后面会讲到。

好的,现在我们来实现IInterfaceBinder对象,概略代码如下:

 public interface IPlus extends IInterface {
         public int add(int a,int b);
}

public class Stub extends Binder {
          @Override
          boolean onTransact(int code, Parcel data, Parcel reply, int flags){
          ......//这里我们覆写了onTransact方法,暂时不用管,后面会讲解。
          }
          ......
}

IInterface plus = new IPlus(){//匿名内部类
 public int add(int a,int b){//定制我们自己的相加方法
             return a+b;
         }
          public IBinder asBinder(){ //实现IInterface中唯一的方法,
                return null ;
           }
};
Binder binder = new Stub();
binder.attachIInterface(plus,"PLUS TWO INT");

step 2: 进程A接收进程B的Binder对象

好了,现在我们有了这个特殊的对象binder,可以在进程B的service中的onBind方法将它返回了,即return binder ;
下面就是见证奇迹的时候。系统会首先收到这个binder对象,然后,它会生成一个BinderProxy(就是前面提到的Binder 的内部类)类的对象,姑且称之为binderproxy,然后将该对象返回给进程A,现在进程A终于在onServiceConnected方法中接收到了binderproxy对象(心情有木有小激动?)。为了下面讲解方便,再次贴出Binder类的概要信息。

public class Binder implement IBinder{
          void attachInterface(IInterface plus, String descriptor)
          IInterface queryLocalInterface(Stringdescriptor) //从IBinder中继承而来
          boolean onTransact(int code, Parcel data, Parcel reply, int flags)//暂时不用管,后面会讲。
          final class BinderProxy implements IBinder {
                    IInterface queryLocalInterface(Stringdescriptor) {
                          return null ;//注意这行代码!!
                                                  //下面会讲到。这行代码只是示例,不是源代码。
                      }
               ......
            }
} 
  • 此时的进程A以为收到的是binder对象,它兴奋了,它迫不及待地要通过queryLocalInterface方法获取这个binderplus对象,利用该对象的加法功能进行加法计算。可结果呢?
  • 首先,binderproxy.queryLocalInterface("PLUS TWO INT")调用是合法的,因为queryLocalInterface方法是IBinder中的方法,而BinderProxyBinder都实现了IBinder接口。但是,binderproxy对象显然没有plus对象,因为它根本就没有attachInterface方法(这是Binder才有滴)。所以,可想而知,进程A的binderproxy.queryLocalInterface("PLUS TWO INT")调用返回的将是一个null(参见上面的示例代码)。

step 3: 进程A利用进程B传过来的对象发起请求

  • 进程A出离愤怒了,我要的是binder,我要的是它里面的plus来帮我完成加法运算,进程B竟然给我一个冒牌货binderproxy(显然,它冤枉了进程B,都是系统惹得祸)。
  • 正在进程A气得头顶冒烟时,binderproxy对象说话了:“别生气进程A,我虽然只是binder对象的代理,但是,我也不是吃素的,你把你的数据(两个int)和你想进行的操作(plus.add)通过我的transact方法(这是在IBinder接口中定义的方法)交给我,我可以替你向binder对象请求你需要的功能,等binder对象把结果给我时,我再把结果交给你不就行了?”
    于是,进程A通过binderproxy对象的transact方法,提交了请求。代码概略如下:
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
int _result;
data.writeInterfaceToken("PLUS TWO INT"); 
 data.writeInt(a); 
data.writeInt(b); 
binderproxy.transact(1, data, reply, 0);//为简单起见,最后一个0暂时不管它

简单解释一下上面代码。data是用来写进程A的数据的(即整数 a和b),reply是准备用来接收结果的。transact方法中的第一个参数是整数1,它是进程A与进程B的一个约定,1就代表想让进程B对进程A传入的数据执行加法操作。这个约定也可以定义在 Stub类中,如下所示:
public static final int ADD = 1;此时,我们可以将binderproxy.transact(1, data, reply, 0);中的1替换为Stub.ADDStub.ADD其实可以是任何整数值的,我们选择1纯属为了简单。

step 4: 进程B收到并处理进程A的请求

  • binderproxy.transact调用发生后,会引起系统的注意,系统意识到binderproxy想找它的真身binder对象执行一个操作了(看!系统其实一直存着binderbinderproxy的对应关系呢!)。于是系统将这个请求中的数据转发给binder对象,binder对象将会在onTransact中收到binderproxy传来的数据(Stub.ADD,data,reply,0),于是它从data中取出进程A传来的数据,又根据Stub.ADD确定进程A想让它执行加法操作,于是它就执行了加法操作,并把结果写回reply。代码概略如下:
 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 Stub.ADD: { 
                      data.enforceInterface("PLUS TWO INT"); 
                        int _arg0; 
                        _arg0 = data.readInt();
                       int _arg1; 
                        _arg1 = data.readInt();
                       int  _result = this.queryLocalIInterface("PLUS TWO INT")
                         .add(_arg0, _arg1); 
                       reply.writeNoException(); 
                        reply.writeInt(_result); 
                        return true; 
                  }
           } 
      return super.onTransact(code, data, reply, flags); 
}

简单解释一下以上代码。我们知道进程A写数据时写入了一个InterfaceToken,就是这行代码
data.writeInterfaceToken("PLUS TWO INT");
这个意思是说,让进程B在自己的binder对象中利用PLUS TWO INT调用queryLocalIInterface方法查找相应的IInterface对象,进程A要执行的操作就在该对象中,至此,我们很容易理解Stub.ADD就代表了plus中的add方法。这是一个二级查找过程,即通过PLUS TWO INT确定要plus来执行功能,通过Stub.ADD确定要执行plus中的add方法。

step 5: 进程A获取进程B返回的处理结果

进程B把结果写入reply后,进程A就可以从reply读取结果了。代码概略如下:

binderproxy.transact(Stub.ADD, data, reply, 0); 
reply.readException(); 
_result = reply.readInt();

-----2016.07.19补充----
好了,借助Android给我们提供的Binder机制,我们成功解决了文章开头提出的问题。但我们可以做得更好一点。比如,我们可以将下面这段代码封装一下。

android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
int _result;
data.writeInterfaceToken("PLUS TWO INT"); 
 data.writeInt(a); 
data.writeInt(b); 
binderproxy.transact(1, data, reply, 0);//为简单起见,最后一个0暂时不管它 
reply.readException(); 
_result = reply.readInt();
具体封装方法是建一个PlusProxy类,如下:

public class PlusProxy implements IPlus {
         private IBinder  binderproxy ;
         public PlusProxy(IBinder binderproxy){
               this.binderproxy = binderproxy ;
         }
         public int add (int a ,int b ){
              android.os.Parcel data = android.os.Parcel.obtain();
               android.os.Parcel reply = android.os.Parcel.obtain();
             data.writeInterfaceToken("PLUS TWO INT"); 
             data.writeInt(a); 
            data.writeInt(b); 
             binderproxy.transact(1, data, reply, 0);
              int _result;
             reply.readException(); 
             _result = reply.readInt();
             return _result ;
         }
}

相关文章

网友评论

      本文标题:Binder 例子

      本文链接:https://www.haomeiwen.com/subject/lrvovftx.html