安卓开发学习之AIDL的使用

背景

这两天开始学习IPC的内容,从AIDL开始

AIDL是安卓接口定义语言的简称,用于进程间通信。现在记录一下使用步骤


步骤

1、建立Person类,实现Parcelable接口

AIDL默认支持的数据类型有:八种基本数据类型、String、List

如果要使用AIDL传递类对象,就必须让类实现Parcelable接口,并且要定义一个方法,名曰readFromParcel

完整的Person代码如下

public class Person implements Parcelable{
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() { // 无参构造方法必须写上,原因后面说
    }

    .. get/set + toString()

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

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


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

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

    protected Person(Parcel in) {
        name = in.readString(); // read和write的顺序最好一致
        age = in.readInt();
    }

    public Person readFromParcel(Parcel parcel) {
        name = parcel.readString();
        age = parcel.readInt();
        return this; 
    }
}

2、在Person类的同一个包下,建立Person.aidl,名字一定要和Person类名一致

这个是为了给aidl引入Person类

// Person.aidl
package com.example.songzeceng.studyofipc;

// 引入Person序列化类
// IPerson.aidl包名要和Person.java包名一样
parcelable Person;

3、也是在这个包下,建立负责IPC的aidl文件

先贴代码

// IPersonManagerInterface.aidl
package com.example.songzeceng.studyofipc;

import com.example.songzeceng.studyofipc.Person;
// 即便是在同一包下,也要手动导入Person类

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

interface IPersonManagerInterface {
    List<Person> getPeople();
    void addPerson(in Person person);
    Person updatePerson(out Person person);
    Person updatePerson2(inout Person person);
}

aidl方法传入自定义类对象,in、out、inout必须写(aidl默认支持的类型不用写,默认且只能是in),否则报错。

关于参数前的in、out和inout,跨进程时,in参数会把参数的内容传给aidl,但其改动不会同步到调用进程;out参数不会把参数的属性传给aidl(aidl获取的参数对象属性为空),但其改动会同步到调用进程;inout参数则是in和out的综合。不跨进程时,三者则是摆设,详情请参见第6步的实际操作


4、编译,生成和aidl同名的java文件(我这儿是IPersonManagerInterface.java)

文件内容一会儿再说


5、编写Service,处理信息

代码如下

public class PeopleService extends Service {
    private static final String TAG = "PeopleService";
    private LinkedList<Person> peopleList = new LinkedList<>();
    private Random random = new Random();

    private final Stub peopleManager = new Stub() { // IPersonManagerInterface.Stub

        @Override
        public List<Person> getPeople() throws RemoteException {
            return peopleList;
        }

        @Override
        public void addPerson(Person person) throws RemoteException {
            boolean isNull = person == null; // 参数为in
            logger("in person is null--" + isNull);
            person.setAge(person.getAge() + 1);
            peopleList.add(person);
        }

        @Override
        public Person updatePerson(Person person) throws RemoteException {
            boolean isNull = person == null; // 参数为out
            logger("out person is null--" + isNull);
            if (isNull) {
                person = new Person();
            } else {
                logger(person.toString());
            }
            person.setAge(random.nextInt() % 40);
            person.setName("jason");
            return person;
        }

        @Override
        public Person updatePerson2(Person person) throws RemoteException {
            boolean isNull = person == null; // 参数为inout
            logger("inout person is null--" + isNull);
            if (isNull) {
                person = new Person();
            } else {
                logger(person.toString());
            }
            person.setAge(random.nextInt() % 40);
            person.setName("mike");
            return person;
        }
    };

    private void logger(String msg) {
        Log.i(TAG, msg);
    }

    @Override
    public void onCreate() {
        Person p = new Person("szc", 21);
        peopleList.add(p);
    }

    @Override
    public IBinder onBind(Intent intent) {
        logger("有连接请求");
        logger(intent.toString());
        return peopleManager; // 返回Stub,因为Stub实现了IBinder接口
    }
}

在清单文件中注册这个Service的时候

要加上exported,以便别的进程获取service;再写上process(进程名),一般就写:remote,这样进程名就是调用方包名:remote

清单文件中注册信息如下

        <service android:name=".PeopleService"
            android:exported="true"
            android:process=":remote">
            <intent-filter>
                <action android:name="com.example.songzeceng"></action>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>


6、MainActivity中使用

6.1、同一个进程下

同一个进程,就是在Service所在项目里的MainActivity里进行绑定,Activity的代码如下

public class MainActivity extends Activity {
    public static final String TAG = "MainActivity";
    private static boolean isConnected = false;

    private Person p = new Person("Dustin", 27);

    private IPersonManagerInterface.Stub peopleManager = null;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            if (peopleManager == null) {
                peopleManager = (IPersonManagerInterface.Stub) IPersonManagerInterface.Stub.asInterface(service); // 此处的service,就是Service的onBind()方法返回的Stub,必须经过这个方法才能还原成Stub类对象
            }

            isConnected = true;

            if (peopleManager != null) {
                try {
                    LinkedList<Person> people = (LinkedList<Person>) peopleManager.getPeople();
                    logger(people.toString());
                    logger("=================");

                    logger("before add");
                    logger(p.toString());
                    logger("=================");
                    peopleManager.addPerson(p); // 验证in
                    logger(p.toString());
                    logger("=================");

                    peopleManager.updatePerson(p); // 验证out
                    logger(p.toString());
                    logger("=================");

                    peopleManager.updatePerson2(p); // 验证in/out
                    logger(p.toString());
                    logger("=================");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }

            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            logger(name + "已经断开连接");
            isConnected = false;
        }
    };

    private void logger(String info) {
        Log.i(TAG, info);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onStop() {
        super.onStop();
        tryDisconnectService();
    }

    @Override
    protected void onStart() {
        super.onStart();
        tryConnectService();
    }

    private void tryConnectService() {
        logger("try to connect service");
        if (!isConnected) {
            Intent intent = new Intent(this, PeopleService.class);
            intent.setAction("com.example.songzeceng");
            bindService(intent, connection, BIND_AUTO_CREATE);
        }
    }

    private void tryDisconnectService() {
        logger("try to disconnect service");
        if (isConnected) {
            unbindService(connection);
            isConnected = false;
        }
    }
}

运行结果如下


为了简洁,我把传入in参数的方法称为in方法,传入out参数的方法称为out方法,传入inout参数的方法称为inout方法

我的Service里的日志都是改变参数前打的,只是看看传入的对象及参数是不是null

in方法:如果像网上说的,in方法传入的对象发生的改变,不会同步到外界,但事实却是外界的实参发生了变化

out方法:如果像网上说的,out方法传入的对象属性为空,但在同进程下,参数并不为空

所以我才觉得,统一进程内,in、out、inout似乎是摆设


6.2、不同进程下

为了验证aidl的跨进程,我新建了一个项目Client,把上面的项目做为Client的模块依赖,具体操作参见文章安卓开发学习之为项目添加模块依赖

MainActivity的代码和刚才的几乎一样,如下

public class MainActivity extends Activity {
    public static final String TAG = "MainActivity";
    private IPersonManagerInterface personManager = null;

    private boolean isConnected = false;
    private Person p = new Person("Dustin", 27);

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            logger(name.toString()); // BinderProxy
            isConnected = true;
            if (service == null) {
                logger("service is null");
                return;
            }

            try {
                logger(service.getClass().getCanonicalName());
                if (personManager == null) {
                    personManager = IPersonManagerInterface.Stub.asInterface(service);
                    // Stub.proxy
                    logger(personManager.getClass().getCanonicalName());
                }
                if (personManager != null) {

                    logger("before add");
                    logger(p.toString());
                    logger("=================");
                    personManager.addPerson(p);
                    logger(p.toString());
                    logger("=================");

                    ArrayList<Person> people = (ArrayList<Person>) personManager.getPeople();
                    // proxy返回的是ArrayList
                    logger(people.toString());
                    logger("=================");

                    personManager.updatePerson(p);
                    // out和inout似乎并不起作用
                    logger(p.toString());
                    logger("=================");

                    personManager.updatePerson2(p);
                    logger(p.toString());
                    logger("=================");

                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isConnected = false;
        }
    };

    private void logger(String s) {
        Log.i(TAG, s);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (!isConnected) {
            Intent intent = new Intent(this, PeopleService.class);
            intent.setAction("com.example.songzeceng");
            bindService(intent, connection, BIND_AUTO_CREATE);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (isConnected) {
            unbindService(connection);
            isConnected = false;
        }
    }
}

运行后Client进程的日志截图


可见,out和inout的参数对象,果然和Service同步发生了变化

再看service进程的日志


out参数的属性确实是空


IPersonManagerInterface#java文件阅读

这个java文件是我们编写IPersonManagerInterface.aidl后,系统编译后生成的,路径是aidl所在工程下的build\generated\source\aidl\debug\包名目录下,如图所示


打开之,格式化(mac的快捷键是option+command+l),这个IPersonManagerInterface继承了IInterface,明显是用来IPC的,而IInterface里方法asBinder()有内部类分别实现,而我们在aidl里定义的四个方法,就放在这个接口里

public interface IPersonManagerInterface extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.example.songzeceng.studyofipc.IPersonManagerInterface {
        ...

        private static class Proxy implements com.example.songzeceng.studyofipc.IPersonManagerInterface {
           ...
        }

        ...
    }

    public java.util.List<com.example.songzeceng.studyofipc.Person> getPeople() throws android.os.RemoteException;

    public void addPerson(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException;

    public com.example.songzeceng.studyofipc.Person updatePerson(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException;

    public com.example.songzeceng.studyofipc.Person updatePerson2(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException;
}

可以看到,这个接口有一个静态内部类,Stub。它是抽象类,继承于Binder,实现了外面的接口IPersonManagerInterface

而Stub也有一个静态内部类,叫做Proxy,但只是实现了外层的接口

下面我花开两朵,各表一枝

Stub

Stub是工作在aidl进程的(我们的Service实例化了一个Stub,所以我们的aidl进程就是Service进程)

源码如下

    public static abstract class Stub extends android.os.Binder implements com.example.songzeceng.studyofipc.IPersonManagerInterface {
        private static final java.lang.String DESCRIPTOR = "com.example.songzeceng.studyofipc.IPersonManagerInterface";
        // 描述符

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

        /**
         * Cast an IBinder object into an com.example.songzeceng.studyofipc.IPersonManagerInterface interface,
         * generating a proxy if needed.
         */
        // 我们在onServiceConnected()方法中,就是用这个asInterface()方法,来把Service的onBind()返回的IBinder对象转化为Stub对象或Proxy对象
        public static com.example.songzeceng.studyofipc.IPersonManagerInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); // 寻找本地Stub接口,也就是当前进程的Stub接口
            // 如果找到了,就表示和aidl是在一个进程里
            // 否则,就不是一个进程里,比如Client
            if (((iin != null) && (iin instanceof com.example.songzeceng.studyofipc.IPersonManagerInterface))) {
                // 在一个进程里,返回的是Service类里的Stub对象
                return ((com.example.songzeceng.studyofipc.IPersonManagerInterface) iin);
            }

            // 不在一个进程里,就返回一个Proxy,所以Client获取的是proxy
            // 传入的参数,就是Service的onBind()方法返回值
            return new com.example.songzeceng.studyofipc.IPersonManagerInterface.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 {
            .. // 此方法仅用于ipc,如果是一个进程里用不到
        }

        private static class Proxy implements com.example.songzeceng.studyofipc.IPersonManagerInterface {
            ...
        }

        static final int TRANSACTION_getPeople = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_updatePerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_updatePerson2 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
    }

可以得出的一个结论就是:如果调用方和aidl在一个进程里,asInterface()获取到的就是Service.onBind()方法的返回结果,调用的就是Service里的Stub的方法,再加上同一进程用的是同一块内存空间,所以才导致in、out、inout没了实际作用


Proxy

Proxy是IPC时,工作在调用方进程的,通过IBinder.transact()方法调用aidl进程的Stub.onTransact()方法

代码如下

        private static class Proxy implements com.example.songzeceng.studyofipc.IPersonManagerInterface {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) { // 构造方法由Stub.asInterface()调用,传过来是Service.onBind()的返回值
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public java.util.List<com.example.songzeceng.studyofipc.Person> getPeople() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain(); // 传入参数
                android.os.Parcel _reply = android.os.Parcel.obtain(); // 返回结果
                java.util.List<com.example.songzeceng.studyofipc.Person> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getPeople, _data, _reply, 0); // 调用Stub.onTransact()
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.songzeceng.studyofipc.Person.CREATOR); // 写入结果
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result; // 返回结果
            }

            @Override
            public void addPerson(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException { // in方法
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((person != null)) {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0); // 把传入参数person写入data,做为Stub.onTransact()的参数
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0); // reply是空,调用Stub.onTransact()
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                } // 没有返回结果
            }

            @Override // out方法
            public com.example.songzeceng.studyofipc.Person updatePerson(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.example.songzeceng.studyofipc.Person _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR); // 并没有写入参数person
                    mRemote.transact(Stub.TRANSACTION_updatePerson, _data, _reply, 0); // 调用Stub.onTransact(),但这时data和reply都是空的
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.example.songzeceng.studyofipc.Person.CREATOR.createFromParcel(_reply); // 根据reply读取结果
                    } else {
                        _result = null;
                    }
                    if ((0 != _reply.readInt())) {
                        person.readFromParcel(_reply); // 根据reply读取person,调用Person.readFromParcel()方法。由于是引用传递,所以会同步到调用方进程
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public com.example.songzeceng.studyofipc.Person updatePerson2(com.example.songzeceng.studyofipc.Person person) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.example.songzeceng.studyofipc.Person _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((person != null)) {
                        _data.writeInt(1);
                        person.writeToParcel(_data, 0); // 写入person参数
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_updatePerson2, _data, _reply, 0); // 调用Stub.onTransact()方法
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.example.songzeceng.studyofipc.Person.CREATOR.createFromParcel(_reply); // 读取结果
                    } else {
                        _result = null;
                    }
                    if ((0 != _reply.readInt())) {
                        person.readFromParcel(_reply); // 读取结果给参数person
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

清楚说明了in、out、inout的工作机制,而且可以看到,关键是调用了Stub.onTransact()方法,代码如下

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            // 这个方法用于IPC,也就是不在一个进程里的情况(Client)
            // 此时this指针指向的也是Service.onBind()的返回值
            // data里面是Client向Service传过来的参数
            // reply则是Service向Client返回的结果

            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getPeople: {
                    // 处理列表
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.example.songzeceng.studyofipc.Person> _result = this.getPeople();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addPerson: {
                    // in方法
                    data.enforceInterface(DESCRIPTOR);
                    com.example.songzeceng.studyofipc.Person _arg0;
                    // 读取参数
                    if ((0 != data.readInt())) {
                        // in方法走这里,调用了Person.CREATOR的createFromParcel()方法
                        _arg0 = com.example.songzeceng.studyofipc.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addPerson(_arg0);
                    // 没有往reply里写东西
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_updatePerson: {
                    // out方法
                    data.enforceInterface(DESCRIPTOR);
                    com.example.songzeceng.studyofipc.Person _arg0;
                    _arg0 = new com.example.songzeceng.studyofipc.Person();
                    // 参数是直接用无参构造方法new出来的,所以IPC时out方法里参数属性是空
                    com.example.songzeceng.studyofipc.Person _result = this.updatePerson(_arg0);
                    // 获取返回值

                    reply.writeNoException();
                    // 写入结果reply
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    if ((_arg0 != null)) {
                        // 似乎还要同步arg0,但arg0的生命周期只在这个方法之内
                        reply.writeInt(1);
                        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                case TRANSACTION_updatePerson2: {
                    // inout方法
                    data.enforceInterface(DESCRIPTOR);
                    com.example.songzeceng.studyofipc.Person _arg0;
                    if ((0 != data.readInt())) {
                        // 读取参数
                        _arg0 = com.example.songzeceng.studyofipc.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    com.example.songzeceng.studyofipc.Person _result = this.updatePerson2(_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);
        }

注释写得很清楚,无需赘言


结语

AIDL是Android跨进程通信的重要手段,应该掌握一下

猜你喜欢

转载自blog.csdn.net/qq_37475168/article/details/80580773