Android Binder 机制学习

概念

Android 中的Binder机制在Android系统框架中发挥着重要的作用,Binder在Android中具体表现为一个类,继承自IBinder接口,具体的功能是实现IPC(跨进程通讯)机制,还可以理解为一种虚拟设备,设备驱动是/dev/binder,Binder 是 ServiceManager和ActivityManagerService、 PackageManagerService以及其他一个服务之间通讯的桥梁。从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当你bindService的时候,服务端会返回一个包含了服务端业务调用的 Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。

例子

这里以一个AIDL的例子来分析Binder的工作原理。

首先定义一个AIDL接口:Book.aidl、IBookManager.aidl 他们的代码如下:

//Book.aidl
// ook.aidl
package com.sample.aidl;

// Declare any non-default types here with import statements

parcelable Book;


//IBookManager.aidl
package com.sample.aidl;

// Declare any non-default types here with import statements
import com.sample.aidl.Book;

interface IBookManager {
   void addBook(in Book book);
   List<Book> getList();
}

上面定义了一个IBookManager接口,里面有两个方法,分别为addBook 和 getList。Book是一个实现了Parcelable接口的对象,跨进程方式传输的对象通常都需要实现Parcelable接口。

public class Book implements Parcelable {
    public String name;

    public Book(String name) {
        this.name = name;
    }

    protected Book(Parcel in) {
        this.name = in.readString();
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.name);
    }
}

上述代码很简单,就是定义了一个接口,然后需要build一下module,之后我们就可以拿来用了。

我这里采用开启一个服务的方式来实现调用AIDL,当然我这里的例子都是在一个应用程序上执行的,所以没有跨进程,但是我们可以以此类推,把服务放在另一个应用程序上也是可以运行的(后面会分析原理)

接下来我们写一个服务:

public class AIDLService extends android.app.Service {
    private static final String TAG = "AIDLService";

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new BookManager();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG,"AIDLService onCreate");
    }

    class BookManager extends IBookManager.Stub {
        List<Book> books = new ArrayList<>();

        @Override
        public void addBook(Book book) throws RemoteException {
            Log.i(TAG, "addBook :" + book.name);
            books.add(book);
        }

        @Override
        public List<Book> getList() throws RemoteException {
            Log.i(TAG, "getList()");
            return books;
        }
    }
}

然后在Activity中启动这个服务,并获得IBookManager接口:

public class AIDLActivity extends AppCompatActivity {
    IBookManager manger;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("onn","onCreate");
        bindService(new Intent(this, AIDLService.class), new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                try {
                    manger = IBookManager.Stub.asInterface(service);
                    manger.addBook(new Book("book11111"));
                    manger.getList();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                manger = null;
            }
        }, BIND_AUTO_CREATE);
    }
}

上面的过程和我们普通使用AIDL的过程是一样的,定义了一个BookManager实现了IBookManger接口,然后实现了对应的方法,在onBind方法中返回这个Binder对象,然后我们在Acitivty中获取这个IBookManager对象然后调了一下它实现的两个方法。(很简单的一个例子)

不必多言,运行结果如下:

10-18 13:39:53.623 26124-26124/com.sample I/AIDLService: AIDLService onCreate
10-18 13:39:53.673 26124-26124/com.sample I/AIDLService: addBook :book11111
10-18 13:39:53.673 26124-26124/com.sample I/AIDLService: getList()

可以看到我们已经实现了一个简单的AIDL例子。

原理分析

大家都知道,在写AIDL的时候,IDE会自动帮我们生成对应的一些代码,这里代码存在的位置是:build->generated->source->aidl 中。我们打开里面的IBookManager文件,里面代码如下:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /android/有心课堂/RecyclerViewSample/app/src/main/aidl/com/sample/aidl/IBookManager.aidl
 */
package com.sample.aidl;

public interface IBookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.sample.aidl.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.sample.aidl.IBookManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.sample.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.sample.aidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.sample.aidl.IBookManager))) {
                return ((com.sample.aidl.IBookManager) iin);
            }
            return new com.sample.aidl.IBookManager.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_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.sample.aidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.sample.aidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.sample.aidl.Book> _result = this.getList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.sample.aidl.IBookManager {
            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;
            }

            @Override
            public void addBook(com.sample.aidl.Book book) 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 ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.util.List<com.sample.aidl.Book> getList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.sample.aidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.sample.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public void addBook(com.sample.aidl.Book book) throws android.os.RemoteException;

    public java.util.List<com.sample.aidl.Book> getList() throws android.os.RemoteException;
}

下面我们一步一步来分析:

首先里面有一个接口IBookManager继承android.os.IInterface接口,然后这个接口里面有个内部类:Stub,Stub里又有一个内部类Proxy

先分析Stub:

Stub继承自Binder对象并且实现了IBookManager接口:

public static abstract class Stub extends android.os.Binder implements com.sample.aidl.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.sample.aidl.IBookManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.sample.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.sample.aidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.sample.aidl.IBookManager))) {
                return ((com.sample.aidl.IBookManager) iin);
            }
            return new com.sample.aidl.IBookManager.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_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.sample.aidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.sample.aidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.sample.aidl.Book> _result = this.getList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

我们聚焦到asInterface(IBinder obj) 方法中,简单的读一读代码,可以理解到里面的代码逻辑是这样的:

首先通过queryLocalInterface方法查询是不是本地的服务,如果是本地的服务就强转成IBookManger并且返回,如果不是就返回Proxy对象。

这样看来我们上面写的例子中调用的就是本地的服务,所以就会直接返回这个对象咯。

我们再看回Activity的代码:

 public void onServiceConnected(ComponentName name, IBinder service){
      manger = IBookManager.Stub.asInterface(service);
 }

这样我们是不是可以很好的理解上面的代码为什么要这样写?

好了,如果是本地的服务就这样很好的理解,但是远程的服务就会相对来说复杂一点:

我们首先来看一看Proxy的代码:

private static class Proxy implements com.sample.aidl.IBookManager {
            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;
            }

            @Override
            public void addBook(com.sample.aidl.Book book) 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 ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.util.List<com.sample.aidl.Book> getList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.sample.aidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.sample.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

当服务是远程服务的时候,如果是远程服务asInterface方法会返回:

return new com.sample.aidl.IBookManager.Stub.Proxy(obj);

返回了一个Proxy对象,然后我们在Activity中调用对应的addBook getList方法的时候,就是Binder大发神威的时候了。假如我们调用了addBook 方法的时候,Binder会调用Proxy中的addBook方法(这个过程运行在Binder线程池中):

  @Override
            public void addBook(com.sample.aidl.Book book) 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 ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

这里首先把book的数据写入_data中然后再调用了一下transact方法。

我们看会Stub中的方法,里面有一个方法叫做onTransact。很明显,调用了transact最终会调用这里的方法。

  @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_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.sample.aidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.sample.aidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.sample.aidl.Book> _result = this.getList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

通过分析,我们看到,他会从data那里把Book对象拿出来,然后调了一下this.addBook()方法,这个方法是一个未实现的方法,具体的实现会在远程服务端中。

通过以上的分析,这个远程调用服务的流程就理顺了,其实在AIDL里面,我们可以简单的把本地和远程的服务称之为客户端和服务端。

整个过程是这样的:首先客户端会发起远程调用,然后就会挂起等待服务端执行完毕并且返回的时候客户端才会继续执行。

拓展

上面我们理顺了AIDL的基本执行流程,其实我们在开发的时候SDK为我们提供了直接写AIDL文件的方式,会自动帮我们生成对应的实现。其实我们完全可以抛弃AIDL文件自己实现对应的功能。

我们完全可以照着生成的代码类比的写出来,我们可以这样做:

首先声明一个接口继承IInterface叫做IBookManger:

interface IBookManager extends andorid.os.IInterface{
   void addBook(in Book book);
   List<Book> getList();
}

然后写一个类IBookManagerImpl,继承自IBookManger,这个类类似于Stub,并且我们可以实现了addBook和getList这两个方法,然后我们在Activity中调用的时候就很简单了。直接:

    manager = IBookMangerImpl.asInterface(service);

在Service的onBind中返回 new BookMangerImpl()即可。

总来的来,Binder在上层应用中只是一中跨进程通讯方式,并且结构设计采用的是C/S结构。

源码地址:https://github.com/Jamlh/Sample

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值