AIDL生成文件的解析

AIDL生成Java文件的解析

上一篇说了kotlin如何使用AIDL进行跨进程通信,这篇文章是对上一篇文章的补充和深入,主要研究依赖AIDL生成的Java文件写了什么。

先附上之前的实体类文件

Person

package com.example.com.testapplication.bean.kotlin

import android.annotation.SuppressLint
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize

@Parcelize
@SuppressLint("ParcelCreator")
class Person(var name: String) : Parcelable {

    override fun toString(): String {
        return " [Person name = " + name + " ]"
    }
}

整理之后的Java生成文件

package com.example.com.testapplication.aidl;

import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.example.com.testapplication.bean.kotlin.Person;

import java.util.List;

public interface IMyAidl extends IInterface {

    void addPerson(com.example.com.testapplication.bean.kotlin.Person person) throws android.os.RemoteException;

	java.util.List<com.example.com.testapplication.bean.kotlin.Person> getPersonList() throws android.os.RemoteException;

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends Binder implements IMyAidl {
        private static final String DESCRIPTOR = "com.example.com.testapplication.IMyAidl";
        static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

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

        /**
         * Cast an IBinder object into an com.example.com.testapplication.IMyAidl interface,
       	 * generating a proxy if needed.
         */
        public static com.example.com.testapplication.aidl.IMyAidl asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.com.testapplication.aidl.IMyAidl))) {
                return ((com.example.com.testapplication.aidl.IMyAidl) iin);
            }
            return new com.example.com.testapplication.aidl.IMyAidl.Stub.Proxy(obj);
        }

        @Override
        public IBinder asBinder() {
            return this;
        }

        /**
         * 返回true则代表访问成功,返回失败则代表访问失败
         *
         * @param code
         * @param data
         * @param reply
         * @param flags
         * @return
         * @throws RemoteException
         */
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION:
                    reply.writeString(DESCRIPTOR);
                    return true;
                case TRANSACTION_addPerson:
                    data.enforceInterface(DESCRIPTOR);
                    Person arg0;
                    if (0 != data.readInt()) {
                        arg0 = (Person) Person.CREATOR.createFromParcel(data);
                    } else {
                        arg0 = null;
                    }
                    this.addPerson(arg0);
                    reply.writeNoException();
                    return true;
                case TRANSACTION_getPersonList:
                    data.enforceInterface(DESCRIPTOR);
                    List<Person> result = getPersonList();
                    reply.writeNoException();
                    reply.writeTypedList(result);
                    return true;
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements IMyAidl {
            private IBinder mRemote;

            public Proxy(IBinder mRemote) {
                this.mRemote = mRemote;
            }

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

            public String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void addPerson(Person person) throws RemoteException {
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                try {
                    data.writeInterfaceToken(DESCRIPTOR);
                    if (null != person) {
                        data.writeInt(1);
                        person.writeToParcel(data, 0);
                    } else {
                        data.writeInt(0);
                    }
                    mRemote.transact(TRANSACTION_addPerson, data, reply, 0);
                    reply.readException();
                } finally {
                    data.recycle();
                    reply.recycle();
                }
            }

            @Override
            public List<Person> getPersonList() throws RemoteException {
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                List<Person> result;
                try {
                    data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(TRANSACTION_getPersonList, data, reply, 0);
                    reply.readException();
                    result = reply.createTypedArrayList(Person.CREATOR);
                } finally {
                    data.recycle();
                    reply.recycle();
                }
                return result;
            }
        }

    }
}

上面是经过整理的生成的Java文件,看上面的代码其实还不够简洁,咱们来拆分一下,上面的文件中有三个类,IMyAidl、Stub、Proxy,我们将这三个类分别写入一个单独的文件。

IMyAidl

package com.example.com.testapplication.aidl;

import android.os.IInterface;

public interface IMyAidl extends IInterface {

    void addPerson(com.example.com.testapplication.bean.kotlin.Person person) throws android.os.RemoteException;

	java.util.List<com.example.com.testapplication.bean.kotlin.Person> getPersonList() throws android.os.RemoteException;

}

Stub

package com.example.com.testapplication.aidl;

import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.example.com.testapplication.bean.kotlin.Person;

import java.util.List;

/**
 * Local-side IPC implementation stub class.
 */
public abstract class Stub extends Binder implements IMyAidl {
    static final String DESCRIPTOR = "com.example.com.testapplication.IMyAidl";
    static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

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

    /**
     * Cast an IBinder object into an com.example.com.testapplication.IMyAidl interface,
     * generating a proxy if needed.
     */
    public static com.example.com.testapplication.aidl.IMyAidl asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.example.com.testapplication.aidl.IMyAidl))) {
            return ((com.example.com.testapplication.aidl.IMyAidl) iin);
        }
        return new Proxy(obj);
    }

    @Override
    public IBinder asBinder() {
        return this;
    }

    /**
     * 返回true则代表访问成功,返回失败则代表访问失败
     *
     * @param code
     * @param data
     * @param reply
     * @param flags
     * @return
     * @throws RemoteException
     */
    @Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;
            case TRANSACTION_addPerson:
                data.enforceInterface(DESCRIPTOR);
                Person arg0;
                if (0 != data.readInt()) {
                    arg0 = (Person) Person.CREATOR.createFromParcel(data);
                } else {
                    arg0 = null;
                }
                this.addPerson(arg0);
                reply.writeNoException();
                return true;
            case TRANSACTION_getPersonList:
                data.enforceInterface(DESCRIPTOR);
                List<Person> result = getPersonList();
                reply.writeNoException();
                reply.writeTypedList(result);
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

}

Proxy

package com.example.com.testapplication.aidl;

import android.os.IBinder;
import android.os.Parcel;
import android.os.RemoteException;

import com.example.com.testapplication.bean.kotlin.Person;

import java.util.List;

import static com.example.com.testapplication.aidl.Stub.DESCRIPTOR;
import static com.example.com.testapplication.aidl.Stub.TRANSACTION_addPerson;
import static com.example.com.testapplication.aidl.Stub.TRANSACTION_getPersonList;

public class Proxy implements IMyAidl {
    private IBinder mRemote;

    public Proxy(IBinder mRemote) {
        this.mRemote = mRemote;
    }

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

    public String getInterfaceDescriptor() {
        return DESCRIPTOR;
    }

    @Override
    public void addPerson(Person person) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            if (null != person) {
                data.writeInt(1);
                person.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            mRemote.transact(TRANSACTION_addPerson, data, reply, 0);
            reply.readException();
        } finally {
            data.recycle();
            reply.recycle();
        }
    }

    @Override
    public List<Person> getPersonList() throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        List<Person> result;
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(TRANSACTION_getPersonList, data, reply, 0);
            reply.readException();
            result = reply.createTypedArrayList(Person.CREATOR);
        } finally {
            data.recycle();
            reply.recycle();
        }
        return result;
    }
}

通过上面拆分出来的三个类我们能看明白,定义了一个拥有Person类中方法名一样的接口类IMyAidl,一个继承Binder实现了IMyAidl接口的抽象类Stub,一个实现了IMyAidl接口的具体实现类Proxy。

IMyAidl

IMyAidl继承IInterface接口,规范接口方法。

Stub这个类是重点,我们看到它继承Binder类就知道使用的是Binder机制。其中有下面几个方法

Stub()

构造方法,内部调用Binder的attachInterface(IInterface owner, String descriptor)方法将Stub当前类(IInterface)和描述写入Binder父类进行绑定

asInterface(android.os.IBinder obj)

我们先来看一下这个方法

/**
 * Cast an IBinder object into an com.example.com.testapplication.IMyAidl interface,
 * generating a proxy if needed.
 */
 public static com.example.com.testapplication.aidl.IMyAidl asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.example.com.testapplication.aidl.IMyAidl))) {
        return ((com.example.com.testapplication.aidl.IMyAidl) iin);
    }
    return new Proxy(obj);
}

可以看到这个方法是将参数传进来的IBinder转成我们定义的接口类,如果需要的话创建代理类。那么什么是需要的时候通常来说非本应用访问就属于需要的时候。

这个类主要也是实现了注释所说的功能,第一步为空判断,判断传进来的参数是否为空不为空的话继续否则退出,第二步查询接口,通过构造方法中attachInterface传入的说明使用Binder的queryLocalInterface方法查询接口,第三步接口转换,查询接口不为空并且是定义的IMyAidl接口类的话就将查询到的接口强转成IMyAidl接口并返回,第四步启动代理,如果第三步判断没有通过则创建Proxy类启动代理模式。

其实呢,这个方法是AIDL为我们生成的,如果我们有其他操作,改变这个方法甚至不要这个方法也是没有问题的,具体的就看你的需求了。

asBinder()

这个方法是属于IInterface接口类的,IInterface接口类是使用Binder时必须要继承的类

我们同样先看一下注释

 /**
 * Retrieve the Binder object associated with this interface.
 * You must use this instead of a plain cast, so that proxy objects
 * can return the correct result.
 */

检索与此接口关联的绑定器对象。必须实现这个对象而不能强制转换,否则代理对象会出现返回对象不正确的情况。

注释写的云里雾里的,并没有说这个方法怎么用,只是说了必须用它。那么我们看一下这个方法的返回值,IBinder,对照上面的asInterface方法,我们可以知道asBinder方法是将实现了IInterface接口的类转成IBinder接口。上面的方法实现直接就返回了本身,因为Stub本身就是继承自Binder,而Binder实现了IBinder。

onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException

同样我们来看下方法

 /**
 * Default implementation is a stub that returns false.  You will want
 * to override this to do the appropriate unmarshalling of transactions.
 *
 * <p>If you want to call this, call transact().
 */
@Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case INTERFACE_TRANSACTION:
                reply.writeString(DESCRIPTOR);
                return true;
            case TRANSACTION_addPerson:
                data.enforceInterface(DESCRIPTOR);
                Person arg0;
                if (0 != data.readInt()) {
                    arg0 = (Person) Person.CREATOR.createFromParcel(data);
                } else {
                    arg0 = null;
                }
                this.addPerson(arg0);
                reply.writeNoException();
                return true;
            case TRANSACTION_getPersonList:
                data.enforceInterface(DESCRIPTOR);
                List<Person> result = getPersonList();
                reply.writeNoException();
                reply.writeTypedList(result);
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

此方法默认返回false,如果你想实现自定义的逻辑需要重写这个方法进行事务处理,这个方法的触发需要调用transact()方法。也就是说这个方法是用来处理我们自定义的事务的,我们自定义的事务是什么,在此篇文章中就是IMyAidl接口的方法。

这个方法在同一进程内通信时不会执行,只有当不同进程间通信时才会执行。

code是行为码:表示要执行的动作,类似Handler.what,IBinder中定义了几个code

  • PING_TRANSACTION,表示要调用 pingBinder() 方法
  • DUMP_TRANSACTION,表示要获取 Binder 内部状态
  • SHELL_COMMAND_TRANSACTION,执行一个 shell 命令
  • INTERFACE_TRANSACTION,询问被调用方的接口描述符号
  • TWEET_TRANSACTION
  • LIKE_TRANSACTION
  • 如果我们需要自定义 code,code 的值范围需要在FIRST_CALL_TRANSACTION(0x00000001) 和 LAST_CALL_TRANSACTION(0x00ffffff) 之间

data是传过来的参数

reply是返回的参数

flag是状态码,表示是否需要阻塞等待返回值,有两个值

  • 0,阻塞
  • FLAG_ONEWAY(0x00000001),表示 Client 的 transact() 是单向调用,执行后立即返回

Proxy

跟Stub实现的功能差不多,但是它是功能代理类,内部持有一个IBinder代理对象,asBinder方法返回的是持有的代理对象。如果是不同进程的话客户端实际使用的接口就是Proxy类,Proxy类将数据进行封装后传给Bidner内核,Binder内核再传递给服务端的Stub类,Stub调用service中实现的方法。

它拥有方法

Proxy(IBinder mRemote)

构造方法,参数为传入的IBinder对象,持有IBinder对象后,进行操作时需要调用mRemote的transact方法

asBinder()

返回持有的IBinder对象

getInterfaceDescriptor()

返回接口的说明

addPerson(Person person)

IMyAidl接口需要实现的方法

@Override
    public void addPerson(Person person) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            data.writeInterfaceToken(DESCRIPTOR);
            if (null != person) {
                data.writeInt(1);
                person.writeToParcel(data, 0);
            } else {
                data.writeInt(0);
            }
            mRemote.transact(TRANSACTION_addPerson, data, reply, 0);
            reply.readException();
        } finally {
            data.recycle();
            reply.recycle();
        }
    }

获取两个Parcel实例,data是要传递的方法参数,reply是方法执行完后传回的参数,首先通过writeInterfaceToken将接口描述写入data,之后向data中写入要传递的数据,写入方式依赖Parcel机制,之后执行mRemote.transact()方法,将行为码(TRANSACTION_addPerson)、data、reply、flag一起传入,四个参数的含义和stub的onTransact的参数含义是一致的,最后要记得释放data和reply的资源

getPersonList()

IMyAidl接口需要实现的方法,实现规则和addPerson()是一致的,只是具体实现逻辑上的不同。

至此,AIDL自动生成的Java文件中的三个类就已经分析完了,之后的文章将讲解Binder机制和Parcel

致敬源码

猜你喜欢

转载自blog.csdn.net/shenglong0210/article/details/89186938
今日推荐