aidl涉及的Binder框架流程简单分析

前言:IPC机制必然会碰到AIDL,网上相关的博客很多,但总不如自己走一边来的印象深刻。

预备的所有文件

关于如何使用AIDL,网上很多也比较简单,不赘述了。

Book.aidl

// Book.aidl
package com.lct.zyw.serviceaidl;

parcelable Book;

Book.java

package com.lct.zyw.serviceaidl;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Created by zhangyawen on 2016/9/14.
 */
public class Book implements Parcelable{
    private int id;
    private String name;

    public Book() {
    }

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

    protected Book(Parcel in) {
        id = in.readInt();
        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];
        }
    };

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    /**
     * Describe the kinds of special objects contained in this Parcelable's
     * marshalled representation.
     *
     * @return a bitmask indicating the set of special object types marshalled
     * by the Parcelable.
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * Flatten this object in to a Parcel.
     *
     * @param dest  The Parcel in which the object should be written.
     * @param flags Additional flags about how the object should be written.
     *              May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(id);
        dest.writeString(name);
    }

    /**
     * 用于AIDL接口方法中Book类型的参数的tag为out,inout。
     * 如果该方法改成空实现,则服务端对参数的任何修改不会改变客户端传入的参数,其实最关键的是搞懂IBookManagerInterface.java的流程,就能知其所以然了。
     * @param source
     */
    public void readFromParcel(Parcel source) {
        id = source.readInt();
        name = source.readString();
    }

    @Override
    public String toString() {
        return "Book { id: "+this.id+", name: "+this.name+"}";
    }
}

IBookManagerinterface.aidl

// IBookManagerInterface.aidl
// android studio 不会自动生成对应aidl文件的java文件,具体做法是在aidl文件下Ctrl+ F9(Make Project)
// build成功之后在对应项目文件路径下的\build\generated\source\aidl\debug中就会生成aidl文件的对应的java文件
package com.lct.zyw.serviceaidl;
//需要手动导入(即使在同一个包下)
import com.lct.zyw.serviceaidl.Book;

interface IBookManagerInterface {
    void doSomething(int x,int y);
    List<Book> getBooks();
    //关于定向Tag,在对应的java文件中需要深入分析
    Book addBookIn(in Book book);
    Book addBookOut(out Book book);
    Book addBookInout(inout Book book);

}

分析IBookManagerinteraface.java

网上很多时从服务端代码往客户端代码分析的,我是从客户端代码往服务端代码分析的,其实都差不多,个人习惯而已

IBookManagerInterface.java

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: D:\\ServiceAIDL\\serviceprot\\src\\main\\aidl\\com\\lct\\zyw\\serviceaidl\\IBookManagerInterface.aidl
 */

/* *
 * Binder的框架:
 * 1.Android Binder框架分为服务器接口、Binder驱动、以及客户端接口;简单想一下,需要提供一个全局服务,
 *   那么全局服务那端即是服务器接口,任何程序即客户端接口,它们之间通过一个Binder驱动访问。
 * 2.服务器端接口:实际上是Binder类的对象,该对象一旦创建,内部则会启动一个隐藏线程,会接收Binder驱动发送的消息,
 *   收到消息后,会执行Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务器端代码。
 * 3.Binder驱动:该对象也为Binder类的实例,客户端通过该对象访问远程服务。
 * 4.客户端接口:获得Binder驱动,调用其transact()发送消息至服务器
 * */

package com.lct.zyw.serviceaidl;

/**
 * 1.接口的继承仍然用[extends]关键字
 * 2.所有可以在Binder中传输的接口都需要继承IInterface接口
 * 3.虽然该Java文件时根据对应的aidl文件由android studio自动生成的,在理解细节实现之后,就可以不依赖aidl文件,
 *   直接手写IBookManagerInterface.java,甚至将该java文件中的代码拆分到服务端和客户端中去。
 *
 */
public interface IBookManagerInterface extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     * 声明了一个Stub的静态抽象内部类,继承了Binder,即使一个Binder的子类,实现IBookManagerInterface接口
     * 说明:1.抽象类实现接口,则接口中的抽象方法可以不在抽象方法中实现,交由继承该抽象类的实现类(非抽象类)来实现,此时就必须实现。
     *      2.若抽象类中实现了接口中的抽象方法,那么实现类(继承自抽象类)就可以不实现,也可以实现(方法的重写)。
     */
    public static abstract class Stub extends android.os.Binder implements com.lct.zyw.serviceaidl.IBookManagerInterface {
        //Binder类(Stub)的唯一标识,一般以类名表示
        private static final java.lang.String DESCRIPTOR = "com.lct.zyw.serviceaidl.IBookManagerInterface";

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

        /**
         * Cast an IBinder object into an com.lct.zyw.serviceaidl.IBookManagerInterface interface,
         * generating a proxy if needed.
         * 将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象
         * obj:客户端绑定服务端成功后,ServiceConnection中的onServiceConnected方法被调用,返回的service(Ibinder接口)
         */
        public static com.lct.zyw.serviceaidl.IBookManagerInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //通过标识,检验obj是否属于本进程(客户端)中的Ibinder
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.lct.zyw.serviceaidl.IBookManagerInterface))) {
                return ((com.lct.zyw.serviceaidl.IBookManagerInterface) iin);
            }
            //进程间通信最终返回客户端的是Stub类中的内部代理类Proxy对象,那么在客户端的任何服务端方法调用都是Proxy类中对应方法的调用。
            return new com.lct.zyw.serviceaidl.IBookManagerInterface.Stub.Proxy(obj);
        }

        /**
         * IInterface接口中的方法 一个Binder类返回一个Ibinder接口(Binder实现了Ibinder接口),
         * 这其实是一种向上转型。
         * @return
         */
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
         * 该方法时通过Proxy类中的transact方法发起的
         * @param code 方法ID:和Proxy类中的transact方法中的一一对应
         * @param data 客户端传递过来的参数
         * @param reply 服务器返回回去的值
         * @param flags 标明是否有返回值,0为有(双向),1为没有(单向)
         * @return
         * @throws android.os.RemoteException
         */
        @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_doSomething: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    this.doSomething(_arg0, _arg1);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getBooks: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.lct.zyw.serviceaidl.Book> _result = this.getBooks();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBookIn: {
                    //与客户端的writeInterfaceToken对用,标识远程服务的名称
                    data.enforceInterface(DESCRIPTOR);
                    //将data转换成Book类
                    com.lct.zyw.serviceaidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    //调用服务端 new IBookManagerInterface.Stub()实现类的具体方法
                    com.lct.zyw.serviceaidl.Book _result = this.addBookIn(_arg0);
                    //将返回值_result转换成 reply,返回给客户端
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                case TRANSACTION_addBookOut: {
                    data.enforceInterface(DESCRIPTOR);
                    com.lct.zyw.serviceaidl.Book _arg0;
                    /**
                     * 相比tag为in,服务端仅仅自己new一个Book类的对象,参数_data并没有对book对象进行赋值操作
                     *              而tag为in的方法是有赋值操作的
                     */
                    _arg0 = new com.lct.zyw.serviceaidl.Book();
                    com.lct.zyw.serviceaidl.Book _result = this.addBookOut(_arg0);
                    reply.writeNoException();
                    /**
                     * 此处将服务端返回的_result(Book)赋值给reply(Parcel)(不管定向Tag是什么,操作都是一样的,不解释)
                     */
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    /**
                     * Note:
                     * 相比tag为in,此处代码是多出来的,这里是将服务端的入参_arg0赋值给reply,那就说服务端方法的入参就是返回值,
                     *              如果服务端方法中不对入参_arg0做任何操作,那么返回的将是一个新new出来的Book对象,需注意。
                     */
                    if ((_arg0 != null)) {
                        reply.writeInt(1);
                        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                case TRANSACTION_addBookInout: {
                    data.enforceInterface(DESCRIPTOR);
                    com.lct.zyw.serviceaidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    com.lct.zyw.serviceaidl.Book _result = this.addBookInout(_arg0);
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    if ((_arg0 != null)) {
                        reply.writeInt(1);
                        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        /**
         * 客户端绑定服务端成功后,执行的直接方法都是内部代理Proxy类中对应的方法
         */
        private static class Proxy implements com.lct.zyw.serviceaidl.IBookManagerInterface {

            private android.os.IBinder mRemote;

            //mRemote被赋值:客户端的传进来的service(Ibinder接口类型)
            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 doSomething(int x, int y) 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.writeInt(x);
                    _data.writeInt(y);
                    mRemote.transact(Stub.TRANSACTION_doSomething, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

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

            @Override
            public com.lct.zyw.serviceaidl.Book addBookIn(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
                /**
                 * _data: 输入型Parcel对象
                 * _reply: 输出型Parcel对象
                 * _result: 返回值对象(类型根据返回值类型而定)
                 */
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.lct.zyw.serviceaidl.Book _result;

                try {
                    //服务器端的enforceInterfac对应
                    _data.writeInterfaceToken(DESCRIPTOR);

                    //将写入参数到_data中去(此处也证明了关于进程间通信的自定义类型的参数对象(Book),必须是实现Parcelable接口的)
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    /**
                     * transact(int code, Parcel data, Parcel reply, int flags)方法对应的参数及自身说明
                     *
                     * code: 方法ID,唯一性,一一对应,服务端onTransact方法会根据客户端传过来的方法ID来确定调用具体的方法
                     * data: 用来存储客户端传递给服务端的Parcel类型的数据
                     * reply: 用来存储客户端传回给客户端的Parcel类型的数据
                     * flags: 是否有返回值:为 0 表示数据可以双向流通,即 _reply 流可以正常的携带数据回来,
                     *                     为 1 的话那么数据将只能单向流通,从服务端回来的 _reply 流将不携带任何数据
                     *        Note:默认为0,不解释。
                     *
                     * 调用该方法来发起RPC(远程过程调用)请求,同时当前线程挂起;然后服务端的onTransact方法会被调用,
                     * 直到RPC过程返回后,当前线程继续执行
                     * Note:此处提到了线程挂起,说明RPC请求很可能是耗时操作,那么在客户端调用服务端的方法时,最好避免直接在UI
                     *       线程中直接调用,以免引起ANR,另起线程更安全
                     */
                    mRemote.transact(Stub.TRANSACTION_addBookIn, _data, _reply, 0);
                    //_reply接受到服务端的数据,转换成返回值的数据类型(Book)返回给客户端
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    /**
                     * Recycle 机制(Note: 并不是java虚拟机的垃圾回收机制)
                     * 简释:
                     * 当一个对象不再使用时把它储藏起来,不让虚拟机回收,需要的时候再从仓库里拿出来重新使用,
                     * 这就避免了对象被回收后再重分配的过程。对于在应用的生命周期内(或者在循环中)需要频繁
                     * 创建的对象来说这个机制特别实用,可以显著减少对象创建的次数,从而减少 GC 的运行时间。
                     * 运用得当便可改善应用的性能。唯一的不足只是需要手动为废弃对象调用 recycle 方法。
                     * 文/TiouLims(简书作者)
                     * 原文链接:http://www.jianshu.com/p/5cba251c7fd9
                     */
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            /**
             * 关于定向Tag(in out inout)的分析(Note:在aidl文件中方法的传入参数设置定向Tag,最根本的还是android studio会根据Tag
             *                                      在对应java代码中方法的实现上有区别)
             * 该方法的传入参数的定向tag在aidl文件中定义为out。
             * @param book
             * @return
             * @throws android.os.RemoteException
             */
            @Override
            public com.lct.zyw.serviceaidl.Book addBookOut(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
                /**
                 * _data: 输入型Parcel对象
                 * _reply: 输出型Parcel对象
                 * _result: 返回值对象(类型根据返回值类型而定)
                 */
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.lct.zyw.serviceaidl.Book _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    /**
                     * 相比tag为in,该方法的入参book并没有转化成_data,通过transcat方法传递给服务端,那就是说,参数定向tag为out的方法
                     * 服务端将不接受的_data参数(Ontransact方法中说明)
                     */
                    mRemote.transact(Stub.TRANSACTION_addBookOut, _data, _reply, 0);
                    _reply.readException();
                    /**
                     * 此处将服务端返回的_reply转化成Book对象,并返回给客户端(不管定向Tag是什么,操作都是一样的,不解释)
                     */
                    if ((0 != _reply.readInt())) {
                        _result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                    /**
                     * 相比tag为in,客户端的入参book被重新赋值,关键在Book.java中需要额外的实现readFromParcel方法,
                     *              那么客户端的入参book到底会有什么变化,主要看readFromParcel方法的具体实现:
                     *              1.参照Book.java中的构造函数Book(Parcel in)实现,那么入参将和返回值相同
                     *              2.readFromParcel是空实现,那么入参没有任何变化
                     */
                    if ((0 != _reply.readInt())) {
                        book.readFromParcel(_reply);
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            /**
             * 定向Tag为inout,则方法实现是同时拥有in和out的并集效果,相对的底层的开销也会比较大,没有必要就不要这么做了。
             * @param book
             * @return
             * @throws android.os.RemoteException
             */
            @Override
            public com.lct.zyw.serviceaidl.Book addBookInout(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.lct.zyw.serviceaidl.Book _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBookInout, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.lct.zyw.serviceaidl.Book.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                    if ((0 != _reply.readInt())) {
                        book.readFromParcel(_reply);
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        //在aidl中定义的方法都会在此得到唯一的ID
        static final int TRANSACTION_doSomething = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_addBookIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_addBookOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        static final int TRANSACTION_addBookInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
    }

    public void doSomething(int x, int y) throws android.os.RemoteException;

    public java.util.List<com.lct.zyw.serviceaidl.Book> getBooks() throws android.os.RemoteException;

    public com.lct.zyw.serviceaidl.Book addBookIn(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;

    public com.lct.zyw.serviceaidl.Book addBookOut(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;

    public com.lct.zyw.serviceaidl.Book addBookInout(com.lct.zyw.serviceaidl.Book book) throws android.os.RemoteException;
}

扩展链接:对Binder的浅显分析及AIDL的使用

结语:好记性不如烂笔头,写下来,方便以后查找。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章