Binder分析

  • 概述

    Binder是Android系统提供的一种IPC(进程间通信)机制,整个android应用层都是基于Binder系统之上的,它就像一个网络,把系统各个服务连接在了一起;当然Android中还有其它的IPC机制,例如Socket,PIC。

    Binder是的通信是C/S架构体系,Server端管理着所有的Client请求;当然在这里还有一个重要的全局服务ServiceManager,它的作用是管理着系统中的各种服务。Server在开户的时候会注册到ServiceManager中去,当Client需要使用这些Service时会通过ServiceManager来拿到这些服务。三都的关系如下:

    屏幕快照 2017-10-23 上午10.24.13

  • 自定义的Binder通信

    AIDL概述:

    在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 显然, Java中是不支持跨进程内存共享的。因此要传递对象, 需要把对象解析成操作系统能够理解的数据格式, 以达到跨界对象访问的目的。在JavaEE中,采用RMI通过序列化传递对象。在Android中, 则采用AIDL(Android Interface Definition Language:接口定义语言)方式实现。

    AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC)。AIDL的IPC机制和EJB所采用的CORBA很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的。

    自定义进程通信:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    1.在A应用中创建一个IDownLoad的aidl文件,然后编译,会在studio的build文件中生成IDownLoad的接口文件
    package com.example.lidaofu.demo;
    // Declare any non-default types here with import statements
    interface IDownLoad {
    void downLoad(String downUrl);
    }
    2.将build的IDownLoad文件复制到B应用中,创建一个服务;在服务的onBinder方法中返回了接口实现类
    public class DownService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
    return new IDownLoad.Stub() {
    @Override//这里是远程端真正"工作"的地方
    public void downLoad(String downUrl) throws RemoteException {
    System.out.println("start down:" + downUrl);
    }
    };
    }
    }
    3.在A应用中开户服务并绑定服务
    public class MainActivity extends AppCompatActivity {
    @Override
    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() {
    @Override
    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();
    }
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
    };
    }

    这样我们就完成了本地端与远程端的通信了,这里我们来看下编译生成的IDownLoad这个接口,这个接口是编译后系统生成,系统帮我们自动生成了Stub,Proxy二个静态内部类,Stub是Service端的代理类,Proxy是Client端的代理类,当我们要调用Service端的方法时不是直接能调用而是通过Client的代理类Proxy来访问Service端的Stub,Stub来调用服务端定义的方法,而这个方法就是我们在onBind方法中创建Stub时实现的方法;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    public 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);
    }
    @Override
    public android.os.IBinder asBinder() {
    return this;
    }
    @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_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;
    }
    @Override
    public android.os.IBinder asBinder() {
    return mRemote;
    }
    public java.lang.String getInterfaceDescriptor() {
    return DESCRIPTOR;
    }
    //当我们调用这个方法时,它内部调用的是mRemote.transact()这个方法,这个
    //方法调用的是父类的方法,具体看下面分析
    @Override
    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都会复写这个方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    /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
    20160909144429620
    我们发现确实是有Stub跟Proxy这二个类,跟自定义Binder通信一样,它们也是通过这二个类来实现Binder的跨进程通信的;
    看下我们的PackageManagerServer类

    1
    public class PackageManagerService extends IPackageManager.Stub

    发现它是继承于IPackageManager.Stub这个类查看代码发现它实现了IPackageManager接口中的方法那么就可以得知当Stub中通过onTransact调用的方法我们的PackageManagerService确实实现了,那么Client端是如何得到这个Server端的引用的呢?
    通过查看源码,最终它会调用到这个方法,返回PackageManagerServer服务的引用;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public 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那拿到的。

坚持原创技术分享,您的支持将鼓励我继续创作!