Android AIDL(Binder) 原理分析_第1张图片

首先介绍 Android 序列化机制、Binder 工作原理,然后再通过创建 AIDL 进行进程间通信,一步步分析 AIDL 原理还有工作流程。

前言

在这里首先介绍 Android 序列化机制、Binder 主要是因为 AIDL 与这两个家伙密切相关,所以我们先了解一下他们,这样有助于我们更好的了解 AIDL 工作原理。

Android 序列化机制

在 Android 系统中关于序列化的方法一般有两种,分别是实现 Serializable 接口和 Parcelable 接口

  • Serializable 是来自 Java 中的序列化接口
  • Parcelable 是 Android 自带的序列化接口

上述的两种序列化接口都有各自不同的优缺点,我们在实际使用时需根据不同情况而定。Serializable 的使用比较简单,创建一个版本号即可;而 Parcelable 则相对复杂一些,会有四个方法需要实现。一般在保存数据到 SD 卡或者网络传输时建议使用 Serializable 即可,虽然效率差一些,好在使用方便。而在运行时数据传递时建议使用 Parcelable,比如 Intent,Bundle 等,Android 底层做了优化处理,效率很高。因此我们下面主要看一下 Parcelable。

Parcelable 使用与分析

首先我们创建一个 Book 对象并且实现 Parcelable接口。

package com.example.zs.ipcdemo.aidl;import android.os.Parcel;import android.os.Parcelable;public class Book implements Parcelable {    private String bookId;    private String bookName;    public String getBookId() {        return bookId;    }    public void setBookId(String bookId) {        this.bookId = bookId;    }    public String getBookName() {        return bookName;    }    public void setBookName(String bookName) {        this.bookName = bookName;    }    public static Creator getCREATOR() {        return CREATOR;    }    public Book(String bookId, String bookName) {        this.bookId = bookId;        this.bookName = bookName;    }    protected Book(Parcel in) {        bookId = in.readString();        bookName = in.readString();    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeString(bookId);        dest.writeString(bookName);    }    public void readFromParcel(Parcel in) {        bookId = in.readString();        bookName = in.readString();    }    @Override    public int describeContents() {        return 0;    }    public static final Creator CREATOR = new Creator() {        @Override        public Book createFromParcel(Parcel in) {            return new Book(in);        }        @Override        public Book[] newArray(int size) {            return new Book[size];        }    };}

我们通过代码可以发现,在实现序列化的过程中主要的功能有序列化、反序列、描述内容,我们可以看出来其实内部都是 Parcel 一系列的 readStringwriteString 方法来实现的,描述内容大部分都返回 0 。Parcel 就是一个存放读取数据的容器,可以在 Binder 中自由传输。这里不对 Parcel 做详细的讲解了有兴趣的可以看看这这篇博客 Android 中 Parcel的分析以及使用

Binder

简单来说 Binder 是 Android 中的一个类,它继承了 IBinder 接口。下面我们从不同角度来说一下 Binder。

  • 从 IPC 角度来说,Binder 是 Android 中的一种跨进程通信方式,Binder 还可以理解为一种虚拟的物理设备,它的设备驱动是 /dev/binder,该通信方式在 linux 中没有。

  • 从 Android Framework 角度来说,Binder 是 ServiceManager 连接各种 Manager(ActivityManager、WindowManager,etc)和相应 ManagerService 的桥梁。

  • 从 Android 应用层来说,Binder 是客户端和服务端进行通信的媒介,当你 bindService 的时候,服务端会返回一个包含了服务端业务调用的 Binder 对象,通过这个 Binder 对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于 AIDL 的服务。

分析 Binder 组成结构

Android AIDL(Binder) 原理分析_第2张图片 摘自田维术的博客

图中的 ServiceManager,负责把 Binder Server 注册到一个容器中,当客户端调用 Server 时 ,Binder驱动去先去 Binder Server 容器中找你要调用的 Server 通知他做那些那些事情。

分析 Binder 通讯流程

Android AIDL(Binder) 原理分析_第3张图片 摘自田维术的博客

从图中我们可以看到,Client 想要直接调用 Server 的 add 方法,是不可以的,因为它们在不同的进程中,这时候就需要 Binder 来帮忙了。

  • 第一步 Server 在 ServiceManager 这个容器中注册。
  • 第二部 Client 想要调用 Server 的 add 方法,就需要先获取 Server 对象, 但是 ServiceManager 不会把真正的 Server 对象返回给 Client,而是把 Server 的一个代理对象返回给 Client ,也就是 Proxy 。
  • 第三部 Client 调用 Proxy 的 add 方法,ServiceManager 会帮他去调用 Server 的 add 方法,并把结果返回给 Client 。

AIDL

我们的主角 AIDL 终于登场了,我们先创建一个简单的 AIDL 然后逐步分析它的工作原理。

创建 Book 类

在主包下面建一个 aidl 包专门存放进程通讯用到的数据, Book 类继承 Parcelable 接口(原因:AIDL 只能传送继承 Parcelable 接口的类)

package com.example.zs.ipcdemo.aidl;import android.os.Parcel;import android.os.Parcelable;public class Book implements Parcelable {    private String bookId;    private String bookName;    public String getBookId() {        return bookId;    }    public void setBookId(String bookId) {        this.bookId = bookId;    }    public String getBookName() {        return bookName;    }    public void setBookName(String bookName) {        this.bookName = bookName;    }    public static Creator getCREATOR() {        return CREATOR;    }    public Book(String bookId, String bookName) {        this.bookId = bookId;        this.bookName = bookName;    }    protected Book(Parcel in) {        bookId = in.readString();        bookName = in.readString();    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeString(bookId);        dest.writeString(bookName);    }    public void readFromParcel(Parcel in) {        bookId = in.readString();        bookName = in.readString();    }    @Override    public int describeContents() {        return 0;    }    public static final Creator CREATOR = new Creator() {        @Override        public Book createFromParcel(Parcel in) {            return new Book(in);        }        @Override        public Book[] newArray(int size) {            return new Book[size];        }    };}

创建 Book.aidl 、IBookManager.aidl

为什么要有 Book.aidl 类,因为只有将类在 aidl 中声明时候,AIDL 才能调用 Book 类。所以说要让 AIDL 能够传送自定义类需要 1、继承 Parcelable 接口 2、创建同名 .aidl 文件声明自己。

package com.example.zs.ipcdemo.aidl;parcelable Book;

IBookManager.aidl 设置让客户端允许调用的接口。

package com.example.zs.ipcdemo.aidl;import com.example.zs.ipcdemo.aidl.Book;interface IBookManager{    List getBookList();    void addBook(in Book book);}

这里面有个地方要注意一下 addBook 方法中有 in 这个类型,下面我们简单说一下 AIDL 文件中 in 类型out 类型和 inout 数据的区别。

  • in :客户端的参数输入:是把实参的值赋值给行参 那么对行参的修改,不会影响实参的值 。
  • out :服务端的参数输入:传递以后,行参和实参都是同一个对象,只是他们名字不同而已,对行参的修改将影响实参的值。
  • inout :这个可以叫输入输出参数,客户端可输入、服务端也可输入。客户端输入了参数到服务端后,服务端也可对该参数进行修改等,最后在客户端上得到的是服务端输出的参数。

我们手动 ReBuild 一下我们的项目系统在这个目录下app\build\generated\source\aidl\debug 为我们自动生成 IBookManger.java ,接下来让我们看一下 IBookManger.java 的庐山真面目。

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: E:\\GitHub\\IPCDemo\\app\\src\\main\\aidl\\com\\example\\zs\\ipcdemo\\aidl\\IBookManager.aidl */package com.example.zs.ipcdemo.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.example.zs.ipcdemo.aidl.IBookManager {        private static final java.lang.String DESCRIPTOR = "com.example.zs.ipcdemo.aidl.IBookManager";        /**         * Construct the stub at attach it to the interface.         */        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        /**         * Cast an IBinder object into an com.example.zs.ipcdemo.aidl.IBookManager interface,         * generating a proxy if needed.         */        public static com.example.zs.ipcdemo.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.example.zs.ipcdemo.aidl.IBookManager))) {                return ((com.example.zs.ipcdemo.aidl.IBookManager) iin);            }            return new com.example.zs.ipcdemo.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_getBookList: {                    data.enforceInterface(DESCRIPTOR);                    java.util.List _result = this.getBookList();                    reply.writeNoException();                    reply.writeTypedList(_result);                    return true;                }                case TRANSACTION_addBook: {                    data.enforceInterface(DESCRIPTOR);                    com.example.zs.ipcdemo.aidl.Book _arg0;                    if ((0 != data.readInt())) {                        _arg0 = com.example.zs.ipcdemo.aidl.Book.CREATOR.createFromParcel(data);                    } else {                        _arg0 = null;                    }                    this.addBook(_arg0);                    reply.writeNoException();                    return true;                }            return super.onTransact(code, data, reply, flags);        }        private static class Proxy implements com.example.zs.ipcdemo.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 java.util.List getBookList() throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                java.util.List _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);                    _reply.readException();                    _result = _reply.createTypedArrayList(com.example.zs.ipcdemo.aidl.Book.CREATOR);                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }            @Override            public void addBook(com.example.zs.ipcdemo.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 void registerListener(com.example.zs.ipcdemo.aidl.IOnNewBookArrivedListener listener) 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.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));                    mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);                    _reply.readException();                } finally {                    _reply.recycle();                    _data.recycle();                }            }            @Override            public void unregisterListener(com.example.zs.ipcdemo.aidl.IOnNewBookArrivedListener listener) 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.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));                    mRemote.transact(Stub.TRANSACTION_unregisterListener, _data, _reply, 0);                    _reply.readException();                } finally {                    _reply.recycle();                    _data.recycle();                }            }        }        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);    }    public java.util.List getBookList() throws android.os.RemoteException;    public void addBook(com.example.zs.ipcdemo.aidl.Book book) throws android.os.RemoteException;}

刚看有点蒙逼,别急我们一步一步分析,IBookManager 文件中,包括 IBookManager.aidl 的两个接口,以及 Stub 和 Proxy 两个实现了 IBookManager 接口的类,其中 Stub 是定义在 IBookManager 接口中的,而 Proxy 则定义在 Stub 类中。


Android AIDL(Binder) 原理分析_第4张图片 IBookManager 类型结构分析

Client 执行流程

1、当服务连接建立完成后,客户端执行代码。IBookManager.Stub.asInterface(service) 这句话方法的作用是判断传入的参数 IBinder 对象和自己是否在同一个进程如果不是则把这个IBinder 参数包装成一个 Proxy 对象,这时调用 Stub 的 sum 方法,间接调用 Proxy 的 getBookList() 方法,我看一下代码。

// 连接服务    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(final ComponentName name, IBinder service) {            //  IBinder 通过 asInterface 判断            //  asInterface方法的作用是判断参数——也就是IBinder对象,和自己是否在同一个进程:            //  是: 则直接转换、直接使用,接下来就跟 Binder 跨进程通信无关啦            //  否: 则把这个IBinder参数包装成一个 Proxy 对象,这时调用 Stub 的方法,间接调用Proxy的方法            IBookManager bookManager = IBookManager.Stub.asInterface(service);            try {                mBookManager = bookManager;                bookManager.getBookList();                Log.d(TAG, "get books count:" + bookManager.getBookList().size());                bookManager.registerListener(arrivedListener);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {            mBookManager = null;        }    };
public static com.example.zs.ipcdemo.aidl.IBookManager asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            // 作用是判断传入的参数 IBinder 对象和自己是否在同一个进程            if (((iin != null) && (iin instanceof com.example.zs.ipcdemo.aidl.IBookManager))) {                return ((com.example.zs.ipcdemo.aidl.IBookManager) iin);            }            return new com.example.zs.ipcdemo.aidl.IBookManager.Stub.Proxy(obj);        }

2、Proxy 在自己的 sumgetBookList() 方法中,会使用 Parcelable 来准备数据,把函数名称、函数参数都写入 _data,让 _reply 接收函数返回值。最后使用 IBinder 的 transact() 方法,把数据就传给 Binder 的 Server 端了。

 @Override            public java.util.List getBookList() throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                java.util.List _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    // 通知服务器调用该方法                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);                    _reply.readException();                    _result = _reply.createTypedArrayList(com.example.zs.ipcdemo.aidl.Book.CREATOR);                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }

Server 执行流程

Server 则是通过 onTransact() 方法接收 Client 进程传过来的数据,包括函数名称、函数参数,找到对应的函数,这里是 getBookList() ,把参数喂进去,得到结果,返回。所以 onTransact() 函数经历了读数据、执行要调用的函数、把执行结果再写数据的过程。

 @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;                }                // 客户端调用指令 getBookList()                case TRANSACTION_getBookList: {                    data.enforceInterface(DESCRIPTOR);                    // 调用本地服务 getBookList() 方法                    java.util.List _result = this.getBookList();                    reply.writeNoException();                    // 将数据传给客户端                    reply.writeTypedList(_result);                    return true;                }                case TRANSACTION_addBook: {                    data.enforceInterface(DESCRIPTOR);                    com.example.zs.ipcdemo.aidl.Book _arg0;                    if ((0 != data.readInt())) {                        _arg0 = com.example.zs.ipcdemo.aidl.Book.CREATOR.createFromParcel(data);                    } else {                        _arg0 = null;                    }                    this.addBook(_arg0);                    reply.writeNoException();                    return true;                }            return super.onTransact(code, data, reply, flags);        }

以上就是 AIDL 的具体原理以及工作流程的分析,希望对大家有所帮助。代码已经更新了GitHub:https://github.com/christian-zs/IPCDemo

更多相关文章

  1. android工程建立到最后一步提示unsupported template dependency
  2. 自己封装的Android sqlite-helper.jar包使用方法
  3. Android四种点击事件方法
  4. Android使用Parcelable传递对象方法及注意事项
  5. Android源码获取方法
  6. Android和J2ME按钮接口之简单对比
  7. Android线程优先级设置方法
  8. 【Android】设置tabhost位于底部的三种方法
  9. Android开发当中Parcelable,Serializable接口的使用

随机推荐

  1. Android中的获取内存信息的相关命令
  2. 【实战】android网页源代码查看器
  3. Android获取StatusBa有效高度
  4. Android中 Bitmap和Drawable相互转换的方
  5. Android(安卓)调用系统的照相,浏览图片,转
  6. Android(安卓)webview 中添加背景
  7. android 获取IP
  8. Android(安卓)将View转换成Bitmap
  9. Android:利用SharedPreferences实现自动登
  10. 获取android手机信息