精华内容
下载资源
问答
  • 刚上班也没什么事,复习一下之前的东西,看了一下Aidl相关的知识,仔细瞅了瞅Aidl调用流程,这里写篇文章整理一下,帮助大家的同时,自己也加深一下印象。对Aidl不太了解的童鞋可以先看一下我之前的一篇文章, ...

            2017年开始上班的第一天,老不想工作了,假期感觉还没开始就已经结束了,唉,时间就是这样,新的一年开始了,虽然很不想干正事,没办法,必须干起来,因为后边的路还很长,距离六十岁还很远。刚上班也没什么事,复习一下之前的东西,看了一下Aidl相关的知识,仔细瞅了瞅Aidl的调用流程,这里写篇文章整理一下,帮助大家的同时,自己也加深一下印象。对Aidl不太了解的童鞋可以先看一下我之前的一篇文章,

    链接如下:http://blog.csdn.net/liuyonglei1314/article/details/54317902 

    案例下载链接:http://download.csdn.net/detail/liuyonglei1314/9734165 

       在上篇文章中我们已经说过了AndroidAidl的简单应用及对象的传递方式,还包含了在AS中进行Aidl开发会遇到的一些问题及决解方法,本篇文章针对使用方法我们不在多说,我们将以传递对象为例深入的剖析Aidl的详细调用流程,继续以上文中传递Person对象为例展开,通过本片文章的学习你会学到以下相关知识:1.Aidl工作调用流程;2.Aidl传递对象时修饰符inoutinout的深入理解。3.对实现Parcelable接口对象中需要实现方法的深入理解。

       首先看一下我们要传递对象的代码:

    public class Person implements Parcelable {
        private String name;
        private int age;
    
        public Person() {
        }
    
        protected Person(Parcel in) {
            name = in.readString();
            age = in.readInt();
        }
    
        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];
            }
        };
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeInt(age);
        }
        public void readFromParcel(Parcel dest) {
            name = dest.readString();
            age = dest.readInt();
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

    Person类实现了parcelable接口,有两个构造方法,有nameage两个成员变量,编译器可以帮我们生成一个staticfinalCREATORwriteToParcel()方法,我们自己写readFromParcel()方法,这里切记顺序要与writeToParcel()方法一直。

    下边再看一下我们的.aidl文件代码:

    import com.jason.aidl.aidldemo.Person;
    interface IMyAidlInterface {
                 String inPerson(in Person p);
                 String outPerson(out Person p);
                 String inOutPerson(inout Person p);
    }

    这里需要注意我们的Person也需要写对应的Person.aidl文件,并在build.gradle中配置,详细信息就不介绍了,上篇文章中进行了详细讲解,在这里看到了修饰符inoutinout,后文会做详细讲解。

    我们继续看Aidl文件生成对应的.java文件的一个整体架构。缩略代码如下:

    public interface IMyAidlInterface extends android.os.IInterface {
        /**
         * Local-side IPC implementation stub class.
         */
        public static abstract class Stub extends android.os.Binder implements com.jason.aidl.aidldemo.IMyAidlInterface {
            private static final java.lang.String DESCRIPTOR = "com.jason.aidl.aidldemo.IMyAidlInterface";
    
            /**
             * Construct the stub at attach it to the interface.
             */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
            /**
             * Cast an IBinder object into an com.jason.aidl.aidldemo.IMyAidlInterface interface,
             * generating a proxy if needed.
             */
            public static com.jason.aidl.aidldemo.IMyAidlInterface asInterface(android.os.IBinder 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 {
    //...
                            return super.onTransact(code, data, reply, flags);
            }
    
            private static class Proxy implements com.jason.aidl.aidldemo.IMyAidlInterface {
                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.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
                   //...                
    return _result;
                }
    
                @Override
                public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
                   //...
                    return _result;
                }
    
                @Override
                public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
                  //...
                    return _result;
                }
            }
    
    
            static final int TRANSACTION_inPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
            static final int TRANSACTION_outPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
            static final int TRANSACTION_inOutPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        }
    
        public java.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException;
    
        public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException;
    
        public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException;
    }

    大致分为三部分,一个interface(最外层的类)两个内部类(StubProxy),先看interface,它跟我们定义的.aidl文件名字相同,继承了IInterface,它的内容分为两部分:一个内部类Stub及我们定义的Person抽象操作方法,每一个都抛出RemoteException。接下来看他的内部类Stub,它是一个抽象类,继承了Binder类,实现了外层的Interface,他比较重要的是一个asInterface()方法和onTransact()方法,它还有一个static的内部类Proxy,它也实现了最外层的Interface,另外有一个需要传递Ibinder的构造函数,还有就是与我们在Aidl类中定义的方法名称相同的方法。这就是我们生成的.java文件的一个大致的架构。由上边可知最外层InterfaceIInterface的子类,而StubProxy是最外层Interface的实现类,Stub继承了Binder,他们之间存在这样的关系。下面我们就对代码的调用流程进行详细讲解。

       首先我们在应用Aidl时通过客户端绑定的方式来获取我们的IMyAidlInterface,并通过它来调用服务端的方法进行通信,如下代码:

    private IMyAidlInterface mService;
    
    Intent intent = new Intent();
    intent.setAction("com.lyl.aidl");
    Intent intent1 = new Intent(createExplicitFromImplicitIntent(this, intent));//兼容5.0以后版本
    bindService(intent1, mServiceC, Context.BIND_AUTO_CREATE);
    
    ServiceConnection mServiceC = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = IMyAidlInterface.Stub.asInterface(service);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
    
        }
    };
    public void do(View v) {
        try {
            Person p1 = new Person();
            p1.setName("刘大");
            p1.setAge(3);
            Person p2 = new Person();
            p2.setName("赵二");
            p2.setAge(3);
            Person p3 = new Person();
            p3.setName("张三");
            p3.setAge(3);
    
            tv.setText("" + mService.inPerson(p1) + "\n" + mService.outPerson(p2) + "\n" + mService.inOutPerson(p3));
    
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    上边代码通过IMyAidlInterface.Stub.asInterface(service)获取了我们的IMyAidlInterface,do()方法中通过它才调用了我们服务端的代码,那我们先看一下这个asInterface方法中的参数是什么,我们知道当通过绑定的方式获取的binder是我们在服务中的onBind()方法中返回的,看一下我们服务端的代码:

    public class MyAidlService extends Service {
    
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("Log_LYL", "Onstart");
            return super.onStartCommand(intent, flags, startId);
    
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return stub;
        }
    
        IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {
           
            @Override
            public String inPerson(Person p) throws RemoteException {
                ...        
    }
    
            @Override
            public String outPerson(Person p) throws RemoteException {
               ...
            }
    
            @Override
            public String inOutPerson(Person p) throws RemoteException {
              ...
            }
        };
    }

    通过以上服务端代码可以知道我们返回的是一个stub 事例,而它是我们的.aidl文件自动生成的.java文件中Stub的一个实例对象。现在我们知道IMyAidlInterface.Stub.asInterface(service)中的service是编译器为我们生成的.java文件的一个实例。我们接着看IMyAidlInterfaceStub.asInterface(service)方法,代码如下:

    public static com.jason.aidl.aidldemo.IMyAidlInterface asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.jason.aidl.aidldemo.IMyAidlInterface))) {
            return ((com.jason.aidl.aidldemo.IMyAidlInterface) iin);
        }
        return new com.jason.aidl.aidldemo.IMyAidlInterface.Stub.Proxy(obj);
    }

    因为我们的Stub类继承了BinderBinder实现了Ibinder接口,所以传递Stub的实例在这里没问题。上述代码首先判断了obj是否为空,不为空往下走,第二个if判断是在判断通过obj获取的iin是否属于当前程序运行的进程,如果是,直接返回,这里也就是说我们要调用的服务与我们当前程序在同一进程,不需要远程通信,直接调用就行。如果不是可以看见系统新生成了一个Proxy对象实例,并把我们的stub实例对象传递进去,看一下Proxy的构造函数,代码如下:

    private android.os.IBinder mRemote;
    
    Proxy(android.os.IBinder remote) {
        mRemote = remote;
    }

    可以看到通过构造函数将Stub实例又一次传到了Proxy中并赋值给mRemotemRemote也变成了Stub实例。

       到这里绑定服务获取ImyAidlInterface对象就介绍完了,做一下总结:通过绑定的方式获取到了在服务端的系统生成的.aidl文件对应的.java文件中子类Stub的一个实例,调用Stub类中的asInterface()方法判断Stub实例所在进程与当前进程是否为同一个,是直接可以操作,不是生成一个Proxy实例并将Stub传入。

       下面我们看一下调用代码实现:

    public void do(View v) {
        try {
            Person p1 = new Person();
            p1.setName("刘大");
            p1.setAge(3);
            Person p2 = new Person();
            p2.setName("赵二");
            p2.setAge(3);
            Person p3 = new Person();
            p3.setName("张三");
            p3.setAge(3);
    
            tv.setText("" + mService.inPerson(p1) + "\n" + mService.outPerson(p2) + "\n" + mService.inOutPerson(p3));
    
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    我们首先分析第一个inPersonp1);这个方法我们在定义时的修饰符为in,及String inPerson(in Person p);下边我们看一下它的实现过程,通过上边的介绍可以知道mService如果不是在同一个进程中是返回的Proxy对象实例,那么我们就进入Proxy中查找对应的方法,代码如下:

    @Override
    public java.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
                if ((p != null)) {
                    _data.writeInt(1);
                    p.writeToParcel(_data, 0);
                } else {
                    _data.writeInt(0);
            }
            mRemote.transact(Stub.TRANSACTION_inPerson, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readString();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    可以看到系统生成了_data_reply两个Parcel类型的对象,然后操作_data,写入Token,判断如果传入的Person对象不为空写入了一个1,接下来调用了person类的writeToParcel()方法,传入了_data,咦,注意这里,我们就知道了实现Parcelable接口时的writeToParcel()方法在这里会用到。好的,我们跟进看一下,代码如下:

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

    这一步看来就是给_data赋值,将我们从开始调用inPerson(com.jason.aidl.aidldemo.Person p)  p实例中的值以writeString、writeInt的方式保存到_data中,如果传入的p为null则写入一个0。继续往下走,我标红的那句是重点,也是这句进行了binder通信,关于binder通讯机制有很多很多东西,由于本人能力有限在这里就不多讲了,感兴趣的可以去了解一下。通过前边可以知道mRemote为asInterface方法传入的service,及Stub实例,而Stub中根本没有transact()方法,而它是Binder的子类,好,我们去Binder中找,还真有:

    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
    
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

    可以看到,在这里调用了onTransact(code, data, reply, flags);我们知道在Stub类中我们实现了这个方法,我们看一下Stub类中的这个方法:

    @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_inPerson: {
                data.enforceInterface(DESCRIPTOR);
                com.jason.aidl.aidldemo.Person _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.jason.aidl.aidldemo.Person.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                java.lang.String _result = this.inPerson(_arg0);
                reply.writeNoException();
                reply.writeString(_result);
                return true;
            }
                            
        }
        return super.onTransact(code, data, reply, flags);
    }

    在这里我们只摘取了inPerson()方法相关内容,先了解一下传入的四个参数:1.code 每一个方法对应一个code,我们定义了多少方法就会有多少code,通过它来做不同处理;2.data、reply为在proxy对应方法中生成的两个Parcel对象;3.flags 一般为1,这个不用管先。 首先通过code找到对应方法的操作,判断data中写入的int值是否为0,不为0说明之前的p不为null,并且已经存入到data中,调用Person中的CREATOR的createFromParcel()方法,咦,这里又用到了Parcelable的东西。看一下这个方法:

    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];
        }
    };
    protected Person(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    可以看到,系统在这里创建了一个Person实例,传入了我们之前的data(保存有person信息),并将data中的信息赋值给新的person对象。回到Stub类的onTransace()方法中继续往下看:

    java.lang.String _result = this.inPerson(_arg0);
    reply.writeNoException();
    reply.writeString(_result);
    return true;

    接着系统调用了this.inPerson(_arg0)方法,_arg0为上边通过createFromParcel()方法新生成的person(包含传递过来的person值)对象,我们知道Stub为抽象类,他实现了我们外层的Interface类,但是没有实现inPerson()方法,那么我们就需要它的子类实现,而我们的mRemote.transace()中的mRemote就是服务端返回的Stub的一个实现子类,也就是说这里调用的this.inPerson(_arg0)方法实际是调用了服务端的那个Stub实现类里边的inPerson()方法,看一下我们之前服务端的inPerson()方法的具体实现:

    @Override
    public String inPerson(Person p) throws RemoteException {
        String old = "name:" + p.getName() + " age:" + p.getAge();
        Log.d("Log_LYL:iPerson_", old);
        p.setName("李四");
        p.setAge(13);
        return "name:" + p.getName() + " age:" + p.getAge();
    }

    通过以上分析,大家就知道了这里的 Log.d("Log_LYL:iPerson_", old); 是为什么有值了,因为我们一开始就将传入的person值附到了Parcel类型的data中了,然后又通过createFromParcel()方法将data中的值付给了新的person对象。 然后回到Stub类的onTransace()方法,会看到调用了reply.writeNoException();reply.writeString(_result);两个方法,将服务端处理完后返回的信息写入了reply中。最后return。再回到Proxy类中的inPerson()方法中:

    mRemote.transact(Stub.TRANSACTION_inPerson, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readString();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;

    将服务端返回的信息读出来并付给_result,最后返回,这样我们在activity中就可以得到服务端处理后的结果了,到现在为止,我们就应该明白了,通过in修饰符传递person后服务端生成了新的person对象,并对新person对象进行处理,这样的话无论服务端如何修改person对象我们客户端的person对象是不会受任何影响的,为什么,因为不是一个对象。这就是in操作符。

    下面我们分析一下out操作符,直接看代码:

    String outPerson(out Person p);//.aidl文件中定义;
    mService.outPerson(p2)//客户端调用;

    我们在客户端直接通过绑定时返回的Stub实例调用outPerson()方法,通过上边对inPerson()方法的介绍我们知道不是同一个进程中的会最终调用到Proxy中相应的方法中,那我们直接看Proxy中代码:

    @Override
    public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_outPerson, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readString();
            if ((0 != _reply.readInt())) {
                p.readFromParcel(_reply);
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    有了上边对inPerson()方法的了解这个相对就简单多了,同样生成两个Parcel对象,作用相同,不同的是用out修饰后没有对传入的pnull判断,也没有取值保存到data中,而是直接调用了mRemote.transact(Stub.TRANSACTION_outPerson, _data, _reply, 0)方法,那好,通过前边我们直接去Stub中找onTransact()方法吧:

    case TRANSACTION_outPerson: {
        data.enforceInterface(DESCRIPTOR);
        com.jason.aidl.aidldemo.Person _arg0;
        _arg0 = new com.jason.aidl.aidldemo.Person();
        java.lang.String _result = this.outPerson(_arg0);
        reply.writeNoException();
        reply.writeString(_result);
        if ((_arg0 != null)) {
            reply.writeInt(1);
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            reply.writeInt(0);
        }
        return true;
    }

    我们只要out方法相关信息,这里系统通过new Person()的方式创建了一个person对象实例赋值个_arg0,这个对象与我们调用时传进来的对象没有一点关系,然后直接调用了this.outPerson()方法,通过inPerson()的介绍我们就知道这里直接调用了服务端outPerson()实现:

    @Override
    public String outPerson(Person p) throws RemoteException {
        //这里的p是空的,因为在IMyAidlInterface的Stub类中onTransact()方法中没有写入;
        // _arg0 = new com.jason.aidl.aidldemo.Person();
        //java.lang.String _result = this.outPerson(_arg0);
        String old = "name:" + p.getName() + " age:" + p.getAge();
        Log.d("Log_LYL:outPerson_", old);
        p.setName("周六");
        p.setAge(20);
        return "name:" + p.getName() + " age:" + p.getAge();
    }

    这里可以知道old是没有赋值的,下面赋值return,回到Stub中:

    java.lang.String _result = this.outPerson(_arg0);
    reply.writeNoException();
    reply.writeString(_result);
    if ((_arg0 != null)) {
        reply.writeInt(1);
        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        reply.writeInt(0);
    }
    return true;

    将处理结果写入reply中,判断_arg0是否为null,不是,写入1,这里又一次用到了writeToParcel()方法,通过上边inPerson()方法介绍可知这里是将_arg0的值付给reply;如果为null,reply中赋值0。回到Proxy中:

    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        mRemote.transact(Stub.TRANSACTION_outPerson, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readString();
        if ((0 != _reply.readInt())) {
            p.readFromParcel(_reply);
        }
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;

    执行完transact()方法后从_reply中读出服务端操作返回结果,判断写入的int值,如果不为0,说明_reply中写入了服务端产生的person信息,则将通过readFromParcel()方法将新的_reply中保存的服务端的person信息写入到客户端传入的p中,这样客户端就收到了服务端的person信息。总结一下out修饰符,即使我们在服务端将p的值写好,服务端也不会接收我们的客户端信息,可以说服务端根本不关系客户端传入的信息,服务端通过new Person()方式产生新对象,并返回给客户端,客户端的p通过readFromParcel()方法获得服务端值,并没有做对象的交换。

    下边看一下inout修饰符代码:

    String inOutPerson(inout Person p);//.aidl文件中;
    mService.inOutPerson(p3);//客户端调用;

    同样看一下Proxy中相应方法:

    @Override
    public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            if ((p != null)) {
                _data.writeInt(1);
                p.writeToParcel(_data, 0);
            } else {
                _data.writeInt(0);
            }
            mRemote.transact(Stub.TRANSACTION_inOutPerson, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readString();
            if ((0 != _reply.readInt())) {
                p.readFromParcel(_reply);
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    case TRANSACTION_inOutPerson: {
        data.enforceInterface(DESCRIPTOR);
        com.jason.aidl.aidldemo.Person _arg0;
        if ((0 != data.readInt())) {
            _arg0 = com.jason.aidl.aidldemo.Person.CREATOR.createFromParcel(data);
        } else {
            _arg0 = null;
        }
        java.lang.String _result = this.inOutPerson(_arg0);
        reply.writeNoException();
        reply.writeString(_result);
        if ((_arg0 != null)) {
            reply.writeInt(1);
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            reply.writeInt(0);
        }
        return true;
    }

    可以看到在inout修饰符时判断了传入的p对象,也就是说服务端接收了保存了客户端的p对象,操作类似与in修饰符,也是通过Person.CREATOR.createFromParcel(data)创建新对象,在Proxy中同样判断了_reply中写入的int值,也就是说,客户端也能收到服务端对对象的修改,也就是说,inout操作符是int、out操作符的合体。到这里相信大家已经明白in、out、inout操作符的区别了,并且连实现都明白了。

       到现在今天要跟大家分享的东西就写完了,相信您看完这篇文章会对Android中的Aidl有一个新的了解,对inoutinout修饰也明白了,对实现Parcelable接口有了新的认识,如果是这样,那我这篇文章就没白写,谢谢大家。



    展开全文
  • 概述AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言。是的,首先我们知道的第一点就是:AIDL是一种语言。既然是一种语言,那么相应的就很自然的衍生出了一些问题:为什么...

    概述

    AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言。是的,首先我们知道的第一点就是:AIDL是一种语言。既然是一种语言,那么相应的就很自然的衍生出了一些问题:

    为什么要设计出这么一门语言?

    它有哪些语法?

    我们应该如何使用它?

    PS:在研究AIDL的时候首先需要熟悉service和序列化的一些知识。

    为什么要设计这门语言?

    AIDL主要是为了实现进程间通信,尤其是在涉及到多进程并发情况下的进程间通信。

    我们都知道,安卓中每一个进程都对应一个Dalvik VM实例,都有一块自己独立的内存,都在自己的内存上存储数据,执行自己的操作,各个进程就像海上的小岛,在同一个世界,但又有自己独立的世界。AIDL就相当于两座岛之间的桥梁,我们使用上帝之手,通过AIDL制定一些规则,,规定他们能进行哪些交流——比如在我们定制的规则下他们只能传输一些固定格式的数据。

    总之,通过这门语言,我们可以愉快的在一个进程访问另一个进程的数据,甚至调用它的一些方法,当然,只能是特定的方法。

    但是,如果仅仅是要进行跨进程通信的话,其实我们还有其他的一些选择,比如 BroadcastReceiver , Messenger 等,但是 BroadcastReceiver 占用的系统资源比较多,如果是频繁的跨进程通信的话显然是不可取的;Messenger 进行跨进程通信时请求队列是同步进行的,无法并发执行,在有些要求多进程的情况下不适用——这种时候就需要使用 AIDL 了。

    它有哪些语法?

    其实AIDL这门语言非常的简单,基本上它的语法和 Java 是一样的,只是在一些细微处有些许差别——毕竟它只是被创造出来简化Android程序员工作的,太复杂不好。所以我们来总结一下它和java的不同之处:

    文件类型:

    用AIDL书写的文件的后缀是 .aidl,而不是 .java。

    数据类型:

    AIDL默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包,就算目标文件与当前正在编写的 .aidl 文件在同一个包下——在 Java 中,这种情况是不需要导包的。比如,现在我们编写了两个文件,一个叫做 Book.java ,另一个叫做 BookManager.aidl,它们都在 com.lypeer.aidldemo 包下 ,现在我们需要在 .aidl 文件里使用 Book 对象,那么我们就必须在 .aidl 文件里面写上 import com.lypeer.aidldemo.Book; 哪怕 .java 文件和 .aidl 文件就在一个包下。

    AIDL默认支持的数据类型包括:

    Java中的八种基本数据类型,包括 byte,short,int,long,float,double,boolean,char。

    String 类型。

    CharSequence类型。

    List类型:List中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable(下文关于这个会有详解)。List可以使用泛型。

    Map类型:Map中的所有元素必须是AIDL支持的类型之一,或者是一个其他AIDL生成的接口,或者是定义的parcelable。Map是不支持泛型的。

    定向tag:

    这是一个极易被忽略的点——这里的“被忽略”指的不是大家都不知道,而是很少人会正确的使用它。在我的理解里,定向 tag 是这样的:AIDL中的定向 tag 表示了在跨进程通信中数据的流向,其中 in 表示数据只能由客户端流向服务端, out 表示数据只能由服务端流向客户端,而 inout 则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in 为定向 tag 的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out 的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout 为定向 tag 的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。

    两种AIDL文件:

    在我的理解里,所有的AIDL文件大致可以分为两类。一类是用来定义parcelable对象,以供其他AIDL文件使用AIDL中非默认支持的数据类型的。一类是用来定义方法接口,以供系统使用来完成跨进程通信的。可以看到,两类文件都是在“定义”些什么,而不涉及具体的实现,这就是为什么它叫做“Android接口定义语言”。

    注:所有的非默认支持数据类型必须通过第一类AIDL文件定义才能被使用。

    下面是两个例子,对于常见的AIDL文件都有所涉及:

    // Book.aidl

    //第一类AIDL文件的例子

    //这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用

    //注意:Book.aidl与Book.java的包名应当是一样的

    package com.lypeer.ipcclient;

    //注意parcelable是小写

    parcelable Book;

    // BookManager.aidl

    //第二类AIDL文件的例子

    package com.lypeer.ipcclient;

    //导入所需要使用的非默认支持数据类型的包

    import com.lypeer.ipcclient.Book;

    interface BookManager {

    //所有的返回值前都不需要加任何东西,不管是什么数据类型

    List getBooks();

    Book getBook();

    int getBookCount();

    //传参时除了Java基本类型以及String,CharSequence之外的类型

    //都需要在前面加上定向tag,具体加什么量需而定

    void setBookPrice(in Book book , int price)

    void setBookName(in Book book , String name)

    void addBookIn(in Book book);

    void addBookOut(out Book book);

    void addBookInout(inout Book book);

    }

    如何使用AIDL文件来完成跨进程通信?

    在进行跨进程通信的时候,在AIDL中定义的方法里包含非默认支持的数据类型与否,我们要进行的操作是不一样的。如果不包含,那么我们只需要编写一个AIDL文件,如果包含,那么我们通常需要写 n+1 个AIDL文件( n 为非默认支持的数据类型的种类数)——显然,包含的情况要复杂一些。所以我接下来将只介绍AIDL文件中包含非默认支持的数据类型的情况,至于另一种简单些的情况相信大家是很容易从中触类旁通的。

    1. 使数据类实现 Parcelable 接口

    由于不同的进程有着不同的内存区域,并且它们只能访问自己的那一块内存区域,所以我们不能像平时那样,传一个句柄过去就完事了——句柄指向的是一个内存区域,现在目标进程根本不能访问源进程的内存,那把它传过去又有什么用呢?所以我们必须将要传输的数据转化为能够在内存之间流通的形式。这个转化的过程就叫做序列化与反序列化。简单来说是这样的:比如现在我们要将一个对象的数据从客户端传到服务端去,我们就可以在客户端对这个对象进行序列化的操作,将其中包含的数据转化为序列化流,然后将这个序列化流传输到服务端的内存中去,再在服务端对这个数据流进行反序列化的操作,从而还原其中包含的数据——通过这种方式,我们就达到了在一个进程中访问另一个进程的数据的目的。

    而通常,在我们通过AIDL进行跨进程通信的时候,选择的序列化方式是实现 Parcelable 接口。关于实现 Parcelable 接口之后里面具体有那些方法啦,每个方法是干嘛的啦,这些我就不展开来讲了,那並非这篇文章的重点,我下面主要讲一下如何快速的生成一个合格的可序列化的类(以Book.java为例)。

    注:若AIDL文件中涉及到的所有数据类型均为默认支持的数据类型,则无此步骤。因为默认支持的那些数据类型都是可序列化的。

    1.1 编译器自动生成

    我当前用的编译器是Android Studio 2.1.3,它是自带了 Parcelable 接口的模板的,只需要我们敲几下键盘就可以轻松的生成一个可序列化的 Parcelable 实现类。

    首先,创建一个类,正常的书写其成员变量,建立getter和setter并添加一个无参构造,比如:

    public class Book{

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    public int getPrice() {

    return price;

    }

    public void setPrice(int price) {

    this.price = price;

    }

    private String name;

    private int price;

    public Book() {}

    }

    然后 implements Parcelable ,接着 as 就会报错,将鼠标移到那里,按下 alt+enter(as默认的自动解决错误的快捷键,如果你们的as有修改过快捷键的话以修改后的为准) 让它自动解决错误,这个时候它会帮你完成一部分的工作:

    54bc60246e67

    在弹出来的框里选择所有的成员变量,然后确定。你会发现类里多了一些代码,但是现在还是会报错,Book下面仍然有一条小横线,再次将鼠标移到那里,按下 alt+enter 让它自动解决错误:

    54bc60246e67

    这次解决完错误之后就不会报错了,这个 Book 类也基本上实现了 Parcelable 接口,可以执行序列化操作了。

    但是请注意,这里有一个坑:默认生成的模板类的对象只支持为 in 的定向 tag 。为什么呢?因为默认生成的类里面只有 writeToParcel() 方法,而如果要支持为 out 或者 inout 的定向 tag 的话,还需要实现 readFromParcel() 方法——而这个方法其实并没有在 Parcelable 接口里面,所以需要我们从头写。

    @Override

    public void writeToParcel(Parcel dest, int flags) {

    dest.writeString(name);

    dest.writeInt(price);

    }

    /**

    * 参数是一个Parcel,用它来存储与传输数据

    * @param dest

    */

    public void readFromParcel(Parcel dest) {

    //注意,此处的读值顺序应当是和writeToParcel()方法中一致的

    name = dest.readString();

    price = dest.readInt();

    }

    像上面这样添加了 readFromParcel() 方法之后,我们的 Book 类的对象在AIDL文件里就可以用 out 或者 inout 来作为它的定向 tag 了。

    此时,完整的 Book 类的代码是这样的:

    package com.lypeer.ipcclient;

    import android.os.Parcel;

    import android.os.Parcelable;

    /**

    * Book.java

    *

    * Created by lypeer on 2016/7/16.

    */

    public class Book implements Parcelable{

    public String getName() {

    return name;

    }

    public void setName(String name) {

    this.name = name;

    }

    public int getPrice() {

    return price;

    }

    public void setPrice(int price) {

    this.price = price;

    }

    private String name;

    private int price;

    public Book(){}

    public Book(Parcel in) {

    name = in.readString();

    price = in.readInt();

    }

    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];

    }

    };

    @Override

    public int describeContents() {

    return 0;

    }

    @Override

    public void writeToParcel(Parcel dest, int flags) {

    dest.writeString(name);

    dest.writeInt(price);

    }

    /**

    * 参数是一个Parcel,用它来存储与传输数据

    * @param dest

    */

    public void readFromParcel(Parcel dest) {

    //注意,此处的读值顺序应当是和writeToParcel()方法中一致的

    name = dest.readString();

    price = dest.readInt();

    }

    //方便打印数据

    @Override

    public String toString() {

    return "name : " + name + " , price : " + price;

    }

    }

    至此,关于AIDL中非默认支持数据类型的序列化操作就完成了。

    1.2 插件生成

    我不是很清楚 Eclipse 或者较低版本的 as 上会不会像 as 2.1.2 这样帮我们在实现 Parcelable 接口的过程中做如此多的操作,但是就算不会,我们还有其他的招数——通过插件来帮我们实现 Parcelable 接口。

    2. 书写AIDL文件

    首先我们需要一个 Book.aidl 文件来将 Book 类引入使得其他的 AIDL 文件其中可以使用 Book 对象。那么第一步,如何新建一个 AIDL 文件呢?Android Studio已经帮我们把这个集成进去了:

    54bc60246e67

    新建AIDL文件

    鼠标移到app上面去,点击右键,然后 new->AIDL->AIDL File,按下鼠标左键就会弹出一个框提示生成AIDL文件了。生成AIDL文件之后,项目的目录会变成这样的:

    54bc60246e67

    建立AIDL文件后的项目目录

    比起以前多了一个叫做 aidl 的包,而且他的层级是和 java 包相同的,并且 aidl 包里默认有着和 java 包里默认的包结构。那么如果你用的是 Eclipse 或者较低版本的 as ,编译器没有这个选项怎么办呢?没关系,我们也可以自己写。打开项目文件夹,依次进入 app->src->main,在 main 包下新建一个和 java 文件夹平级的 aidl 文件夹,然后我们手动在这个文件夹里面新建和 java 文件夹里面的默认结构一样的文件夹结构,再在最里层新建 .aidl 文件就可以了:

    54bc60246e67

    自己新建AIDL文件的目录

    注意看图中的文件目录。

    Ok,如何新建AIDL文件说的差不多了,接下来就该写AIDL文件的内容了。内容的话如果上一节有认真看的话基本上是没什么问题的。在这里,我们需要两个AIDL文件,我是这样写的:

    // Book.aidl

    //第一类AIDL文件

    //这个文件的作用是引入了一个序列化对象 Book 供其他的AIDL文件使用

    //注意:Book.aidl与Book.java的包名应当是一样的

    package com.lypeer.ipcclient;

    //注意parcelable是小写

    parcelable Book;

    // BookManager.aidl

    //第二类AIDL文件

    //作用是定义方法接口

    package com.lypeer.ipcclient;

    //导入所需要使用的非默认支持数据类型的包

    import com.lypeer.ipcclient.Book;

    interface BookManager {

    //所有的返回值前都不需要加任何东西,不管是什么数据类型

    List getBooks();

    //传参时除了Java基本类型以及String,CharSequence之外的类型

    //都需要在前面加上定向tag,具体加什么量需而定

    void addBook(in Book book);

    }

    注意:这里又有一个坑!大家可能注意到了,在 Book.aidl 文件中,我一直在强调:Book.aidl与Book.java的包名应当是一样的。这似乎理所当然的意味着这两个文件应当是在同一个包里面的——事实上,很多比较老的文章里就是这样说的,他们说最好都在 aidl 包里同一个包下,方便移植——然而在 Android Studio 里并不是这样。如果这样做的话,系统根本就找不到 Book.java 文件,从而在其他的AIDL文件里面使用 Book 对象的时候会报 Symbol not found 的错误。为什么会这样呢?因为 Gradle 。大家都知道,Android Studio 是默认使用 Gradle 来构建 Android 项目的,而 Gradle 在构建项目的时候会通过 sourceSets 来配置不同文件的访问路径,从而加快查找速度——问题就出在这里。Gradle 默认是将 java 代码的访问路径设置在 java 包下的,这样一来,如果 java 文件是放在 aidl 包下的话那么理所当然系统是找不到这个 java 文件的。那应该怎么办呢?

    又要 java文件和 aidl 文件的包名是一样的,又要能找到这个 java 文件——那么仔细想一下的话,其实解决方法是很显而易见的。首先我们可以把问题转化成:如何在保证两个文件包名一样的情况下,让系统能够找到我们的 java 文件?这样一来思路就很明确了:要么让系统来 aidl 包里面来找 java 文件,要么把 java 文件放到系统能找到的地方去,也即放到 java 包里面去。接下来我详细的讲一下这两种方式具体应该怎么做:

    修改 build.gradle 文件:在 android{} 中间加上下面的内容:

    sourceSets {

    main {

    java.srcDirs = ['src/main/java', 'src/main/aidl']

    }

    }

    也就是把 java 代码的访问路径设置成了 java 包和 aidl 包,这样一来系统就会到 aidl 包里面去查找 java 文件,也就达到了我们的目的。只是有一点,这样设置后 Android Studio 中的项目目录会有一些改变,我感觉改得挺难看的。

    把 java 文件放到 java 包下去:把 Book.java 放到 java 包里任意一个包下,保持其包名不变,与 Book.aidl 一致。只要它的包名不变,Book.aidl 就能找到 Book.java ,而只要 Book.java 在 java 包下,那么系统也是能找到它的。但是这样做的话也有一个问题,就是在移植相关 .aidl 文件和 .java 文件的时候没那么方便,不能直接把整个 aidl 文件夹拿过去完事儿了,还要单独将 .java 文件放到 java 文件夹里去。

    我们可以用上面两个方法之一来解决找不到 .java 文件的坑,具体用哪个就看大家怎么选了,反正都挺简单的。

    到这里我们就已经将AIDL文件新建并且书写完毕了,clean 一下项目,如果没有报错,这一块就算是大功告成了。

    3. 移植相关文件

    我们需要保证,在客户端和服务端中都有我们需要用到的 .aidl 文件和其中涉及到的 .java 文件,因此不管在哪一端写的这些东西,写完之后我们都要把这些文件复制到另一端去。如果是用的上面两个方法中的第一个解决的找不到 .java 文件的问题,那么直接将 aidl 包复制到另一端的 main 目录下就可以了;如果是使用第二个方法的话,就除了把把整个 aidl 文件夹拿过去,还要单独将 .java 文件放到 java 文件夹里去。

    4. 编写服务端代码

    通过上面几步,我们已经完成了AIDL及其相关文件的全部内容,那么我们究竟应该如何利用这些东西来进行跨进程通信呢?其实,在我们写完AIDL文件并 clean 或者 rebuild 项目之后,编译器会根据AIDL文件为我们生成一个与AIDL文件同名的 .java 文件,这个 .java 文件才是与我们的跨进程通信密切相关的东西。事实上,基本的操作流程就是:在服务端实现AIDL中定义的方法接口的具体逻辑,然后在客户端调用这些方法接口,从而达到跨进程通信的目的。

    接下来我直接贴上我写的服务端代码:

    /**

    * 服务端的AIDLService.java

    *

    * Created by lypeer on 2016/7/17.

    */

    public class AIDLService extends Service {

    public final String TAG = this.getClass().getSimpleName();

    //包含Book对象的list

    private List mBooks = new ArrayList<>();

    //由AIDL文件生成的BookManager

    private final BookManager.Stub mBookManager = new BookManager.Stub() {

    @Override

    public List getBooks() throws RemoteException {

    synchronized (this) {

    Log.e(TAG, "invoking getBooks() method , now the list is : " + mBooks.toString());

    if (mBooks != null) {

    return mBooks;

    }

    return new ArrayList<>();

    }

    }

    @Override

    public void addBook(Book book) throws RemoteException {

    synchronized (this) {

    if (mBooks == null) {

    mBooks = new ArrayList<>();

    }

    if (book == null) {

    Log.e(TAG, "Book is null in In");

    book = new Book();

    }

    //尝试修改book的参数,主要是为了观察其到客户端的反馈

    book.setPrice(2333);

    if (!mBooks.contains(book)) {

    mBooks.add(book);

    }

    //打印mBooks列表,观察客户端传过来的值

    Log.e(TAG, "invoking addBooks() method , now the list is : " + mBooks.toString());

    }

    }

    };

    @Override

    public void onCreate() {

    super.onCreate();

    Book book = new Book();

    book.setName("Android开发艺术探索");

    book.setPrice(28);

    mBooks.add(book);

    }

    @Nullable

    @Override

    public IBinder onBind(Intent intent) {

    Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString()));

    return mBookManager;

    }

    }

    整体的代码结构很清晰,大致可以分为三块:第一块是初始化。在 onCreate() 方法里面我进行了一些数据的初始化操作。第二块是重写 BookManager.Stub 中的方法。在这里面提供AIDL里面定义的方法接口的具体实现逻辑。第三块是重写 onBind() 方法。在里面返回写好的 BookManager.Stub 。

    接下来在 Manefest 文件里面注册这个我们写好的 Service ,这个不写的话我们前面做的工作都是无用功:

    android:name=".service.AIDLService"

    android:exported="true">

    5. 编写客户端代码

    前面说过,在客户端我们要完成的工作主要是调用服务端的方法,但是在那之前,我们首先要连接上服务端,完整的客户端代码是这样的:

    /**

    * 客户端的AIDLActivity.java

    * 由于测试机的无用debug信息太多,故log都是用的e

    *

    * Created by lypeer on 2016/7/17.

    */

    public class AIDLActivity extends AppCompatActivity {

    //由AIDL文件生成的Java类

    private BookManager mBookManager = null;

    //标志当前与服务端连接状况的布尔值,false为未连接,true为连接中

    private boolean mBound = false;

    //包含Book对象的list

    private List mBooks;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_aidl);

    }

    /**

    * 按钮的点击事件,点击之后调用服务端的addBookIn方法

    *

    * @param view

    */

    public void addBook(View view) {

    //如果与服务端的连接处于未连接状态,则尝试连接

    if (!mBound) {

    attemptToBindService();

    Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show();

    return;

    }

    if (mBookManager == null) return;

    Book book = new Book();

    book.setName("APP研发录In");

    book.setPrice(30);

    try {

    mBookManager.addBook(book);

    Log.e(getLocalClassName(), book.toString());

    } catch (RemoteException e) {

    e.printStackTrace();

    }

    }

    /**

    * 尝试与服务端建立连接

    */

    private void attemptToBindService() {

    Intent intent = new Intent();

    intent.setAction("com.lypeer.aidl");

    intent.setPackage("com.lypeer.ipcserver");

    bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);

    }

    @Override

    protected void onStart() {

    super.onStart();

    if (!mBound) {

    attemptToBindService();

    }

    }

    @Override

    protected void onStop() {

    super.onStop();

    if (mBound) {

    unbindService(mServiceConnection);

    mBound = false;

    }

    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {

    @Override

    public void onServiceConnected(ComponentName name, IBinder service) {

    Log.e(getLocalClassName(), "service connected");

    mBookManager = BookManager.Stub.asInterface(service);

    mBound = true;

    if (mBookManager != null) {

    try {

    mBooks = mBookManager.getBooks();

    Log.e(getLocalClassName(), mBooks.toString());

    } catch (RemoteException e) {

    e.printStackTrace();

    }

    }

    }

    @Override

    public void onServiceDisconnected(ComponentName name) {

    Log.e(getLocalClassName(), "service disconnected");

    mBound = false;

    }

    };

    }

    同样很清晰,首先建立连接,然后在 ServiceConnection 里面获取 BookManager 对象,接着通过它来调用服务端的方法。

    6. 开始通信吧!

    通过上面的步骤,我们已经完成了所有的前期工作,接下来就可以通过AIDL来进行跨进程通信了!将两个app同时运行在同一台手机上,然后调用客户端的 addBook() 方法,我们会看到服务端的 logcat 信息是这样的:

    //服务端的 log 信息,我把无用的信息头去掉了,然后给它编了个号

    1,on bind,intent = Intent { act=com.lypeer.aidl pkg=com.lypeer.ipcserver }

    2,invoking getBooks() method , now the list is : [name : Android开发艺术探索 , price : 28]

    3,invoking addBooks() method , now the list is : [name : Android开发艺术探索 , price : 28, name : APP研发录In , price : 2333]

    客户端的信息是这样的:

    //客户端的 log 信息

    service connected

    [name : Android开发艺术探索 , price : 28]

    name : APP研发录In , price : 30

    所有的 log 信息都很正常并且符合预期——这说明我们到这里为止的步骤都是正确的,按照上面说的来做是能够正确的使用AIDL来进行跨进程通信的。

    展开全文
  • 2017年開始上班的第一天。老不想工作了,假期感觉还没開始就已经结束了,唉,时间就是...细致瞅了瞅Aidl调用流程,这里写篇文章整理一下。帮助大家的同一时候。自己也加深一下印象。对Aidl不太了解的童鞋能够先...

            2017年開始上班的第一天。老不想工作了,假期感觉还没開始就已经结束了,唉,时间就是这样,新的一年開始了,尽管非常不想干正事,没办法,必须干起来。由于后边的路还非常长,距离六十岁还非常远。

    刚上班也没什么事,复习一下之前的东西,看了一下Aidl相关的知识。细致瞅了瞅Aidl的调用流程,这里写篇文章整理一下。帮助大家的同一时候。自己也加深一下印象。对Aidl不太了解的童鞋能够先看一下我之前的一篇文章,

    链接例如以下:http://blog.csdn.net/liuyonglei1314/article/details/54317902 

    案例下载链接:http://download.csdn.net/detail/liuyonglei1314/9734165 

       在上篇文章中我们已经说过了AndroidAidl的简单应用及对象的传递方式,还包括了在AS中进行Aidl开发会遇到的一些问题及决解方法,本篇文章针对用法我们不在多说。我们将以传递对象为例深入的剖析Aidl的具体调用流程,继续以上文中传递Person对象为例展开,通过本片文章的学习你会学到下面相关知识:1.Aidl工作调用流程;2.Aidl传递对象时修饰符inoutinout的深入理解。3.对实现Parcelable接口对象中须要实现方法的深入理解。

       首先看一下我们要传递对象的代码:

    public class Person implements Parcelable {
        private String name;
        private int age;
    
        public Person() {
        }
    
        protected Person(Parcel in) {
            name = in.readString();
            age = in.readInt();
        }
    
        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];
            }
        };
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeInt(age);
        }
        public void readFromParcel(Parcel dest) {
            name = dest.readString();
            age = dest.readInt();
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

    Person类实现了parcelable接口。有两个构造方法,有nameage两个成员变量。编译器能够帮我们生成一个staticfinalCREATORwriteToParcel()方法,我们自己写readFromParcel()方法,这里切记顺序要与writeToParcel()方法一直。

    下边再看一下我们的.aidl文件代码:

    import com.jason.aidl.aidldemo.Person;
    interface IMyAidlInterface {
                 String inPerson(in Person p);
                 String outPerson(out Person p);
                 String inOutPerson(inout Person p);
    }

    这里须要注意我们的Person也须要写相应的Person.aidl文件,并在build.gradle中配置。具体信息就不介绍了。上篇文章中进行了具体解说。在这里看到了修饰符inoutinout。后文会做具体解说。

    我们继续看Aidl文件生成相应的.java文件的一个总体架构。缩略代码例如以下:

    public interface IMyAidlInterface extends android.os.IInterface {
        /**
         * Local-side IPC implementation stub class.
         */
        public static abstract class Stub extends android.os.Binder implements com.jason.aidl.aidldemo.IMyAidlInterface {
            private static final java.lang.String DESCRIPTOR = "com.jason.aidl.aidldemo.IMyAidlInterface";
    
            /**
             * Construct the stub at attach it to the interface.
             */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
            /**
             * Cast an IBinder object into an com.jason.aidl.aidldemo.IMyAidlInterface interface,
             * generating a proxy if needed.
             */
            public static com.jason.aidl.aidldemo.IMyAidlInterface asInterface(android.os.IBinder 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 {
    //...
                            return super.onTransact(code, data, reply, flags);
            }
    
            private static class Proxy implements com.jason.aidl.aidldemo.IMyAidlInterface {
                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.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
                   //...                
    return _result;
                }
    
                @Override
                public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
                   //...
                    return _result;
                }
    
                @Override
                public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
                  //...
                    return _result;
                }
            }
    
    
            static final int TRANSACTION_inPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
            static final int TRANSACTION_outPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
            static final int TRANSACTION_inOutPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        }
    
        public java.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException;
    
        public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException;
    
        public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException;
    }

    大致分为三部分。一个interface(最外层的类)两个内部类(StubProxy)。先看interface。它跟我们定义的.aidl文件名称字同样,继承了IInterface,它的内容分为两部分:一个内部类Stub及我们定义的Person抽象操作方法。每个都抛出RemoteException。接下来看他的内部类Stub,它是一个抽象类。继承了Binder类,实现了外层的Interface,他比較重要的是一个asInterface()方法和onTransact()方法。它另一个static的内部类Proxy。它也实现了最外层的Interface,另外有一个须要传递Ibinder的构造函数,还有就是与我们在Aidl类中定义的方法名称同样的方法。这就是我们生成的.java文件的一个大致的架构。

    由上边可知最外层InterfaceIInterface的子类,而StubProxy是最外层Interface的实现类。Stub继承了Binder,他们之间存在这种关系。以下我们就对代码的调用流程进行具体解说。

       首先我们在应用Aidl时通过client绑定的方式来获取我们的IMyAidlInterface,并通过它来调用服务端的方法进行通信,例如以下代码:

    private IMyAidlInterface mService;
    
    Intent intent = new Intent();
    intent.setAction("com.lyl.aidl");
    Intent intent1 = new Intent(createExplicitFromImplicitIntent(this, intent));//兼容5.0以后版本号
    bindService(intent1, mServiceC, Context.BIND_AUTO_CREATE);
    
    ServiceConnection mServiceC = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = IMyAidlInterface.Stub.asInterface(service);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
    
        }
    };
    public void do(View v) {
        try {
            Person p1 = new Person();
            p1.setName("刘大");
            p1.setAge(3);
            Person p2 = new Person();
            p2.setName("赵二");
            p2.setAge(3);
            Person p3 = new Person();
            p3.setName("张三");
            p3.setAge(3);
    
            tv.setText("" + mService.inPerson(p1) + "\n" + mService.outPerson(p2) + "\n" + mService.inOutPerson(p3));
    
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    上边代码通过IMyAidlInterface.Stub.asInterface(service)获取了我们的IMyAidlInterface,do()方法中通过它才调用了我们服务端的代码,那我们先看一下这个asInterface方法中的參数是什么,我们知道当通过绑定的方式获取的binder是我们在服务中的onBind()方法中返回的,看一下我们服务端的代码:

    public class MyAidlService extends Service {
    
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("Log_LYL", "Onstart");
            return super.onStartCommand(intent, flags, startId);
    
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return stub;
        }
    
        IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {
           
            @Override
            public String inPerson(Person p) throws RemoteException {
                ...        
    }
    
            @Override
            public String outPerson(Person p) throws RemoteException {
               ...
            }
    
            @Override
            public String inOutPerson(Person p) throws RemoteException {
              ...
            }
        };
    }

    通过以上服务端代码能够知道我们返回的是一个stub 事例。而它是我们的.aidl文件自己主动生成的.java文件里Stub的一个实例对象。如今我们知道IMyAidlInterface.Stub.asInterface(service)中的service是编译器为我们生成的.java文件的一个实例。我们接着看IMyAidlInterfaceStub.asInterface(service)方法,代码例如以下:

    public static com.jason.aidl.aidldemo.IMyAidlInterface asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof com.jason.aidl.aidldemo.IMyAidlInterface))) {
            return ((com.jason.aidl.aidldemo.IMyAidlInterface) iin);
        }
        return new com.jason.aidl.aidldemo.IMyAidlInterface.Stub.Proxy(obj);
    }

    由于我们的Stub类继承了BinderBinder实现了Ibinder接口,所以传递Stub的实例在这里没问题。上述代码首先推断了obj是否为空,不为空往下走,第二个if推断是在推断通过obj获取的iin是否属于当前程序执行的进程。假设是,直接返回,这里也就是说我们要调用的服务与我们当前程序在同一进程。不须要远程通信,直接调用即可。假设不是能够看见系统新生成了一个Proxy对象实例,并把我们的stub实例对象传递进去。看一下Proxy的构造函数,代码例如以下:

    private android.os.IBinder mRemote;
    
    Proxy(android.os.IBinder remote) {
        mRemote = remote;
    }

    能够看到通过构造函数将Stub实例重新传到了Proxy中并赋值给mRemotemRemote也变成了Stub实例。

       到这里绑定服务获取ImyAidlInterface对象就介绍完了,做一下总结:通过绑定的方式获取到了在服务端的系统生成的.aidl文件相应的.java文件里子类Stub的一个实例,调用Stub类中的asInterface()方法推断Stub实例所在进程与当前进程是否为同一个。是直接能够操作。不是生成一个Proxy实例并将Stub传入。

       以下我们看一下调用代码实现:

    public void do(View v) {
        try {
            Person p1 = new Person();
            p1.setName("刘大");
            p1.setAge(3);
            Person p2 = new Person();
            p2.setName("赵二");
            p2.setAge(3);
            Person p3 = new Person();
            p3.setName("张三");
            p3.setAge(3);
    
            tv.setText("" + mService.inPerson(p1) + "\n" + mService.outPerson(p2) + "\n" + mService.inOutPerson(p3));
    
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    我们首先分析第一个inPersonp1);这种方法我们在定义时的修饰符为in,及String inPerson(in Person p);下边我们看一下它的实现过程,通过上边的介绍能够知道mService假设不是在同一个进程中是返回的Proxy对象实例,那么我们就进入Proxy中查找相应的方法,代码例如以下:

    @Override
    public java.lang.String inPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
                if ((p != null)) {
                    _data.writeInt(1);
                    p.writeToParcel(_data, 0);
                } else {
                    _data.writeInt(0);
            }
            mRemote.transact(Stub.TRANSACTION_inPerson, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readString();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    能够看到系统生成了_data_reply两个Parcel类型的对象,然后操作_data。写入Token,推断假设传入的Person对象不为空写入了一个1,接下来调用了person类的writeToParcel()方法,传入了_data,咦,注意这里。我们就知道了实现Parcelable接口时的writeToParcel()方法在这里会用到。好的,我们跟进看一下,代码例如以下:

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

    这一步看来就是给_data赋值,将我们从開始调用inPerson(com.jason.aidl.aidldemo.Person p)  p实例中的值以writeString、writeInt的方式保存到_data中,假设传入的p为null则写入一个0。继续往下走。我标红的那句是重点,也是这句进行了binder通信,关于binder通讯机制有非常多非常多东西。因为本人能力有限在这里就不多讲了,感兴趣的能够去了解一下。通过前边能够知道mRemote为asInterface方法传入的service,及Stub实例,而Stub中根本没有transact()方法,而它是Binder的子类,好,我们去Binder中找,还真有:

    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
    
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

    能够看到。在这里调用了onTransact(code, data, reply, flags);我们知道在Stub类中我们实现了这种方法,我们看一下Stub类中的这种方法:

    @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_inPerson: {
                data.enforceInterface(DESCRIPTOR);
                com.jason.aidl.aidldemo.Person _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.jason.aidl.aidldemo.Person.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                java.lang.String _result = this.inPerson(_arg0);
                reply.writeNoException();
                reply.writeString(_result);
                return true;
            }
                            
        }
        return super.onTransact(code, data, reply, flags);
    }

    在这里我们仅仅摘取了inPerson()方法相关内容。先了解一下传入的四个參数:1.code 每个方法相应一个code,我们定义了多少方法就会有多少code,通过它来做不同处理;2.data、reply为在proxy相应方法中生成的两个Parcel对象。3.flags 一般为1。这个不用管先。 首先通过code找到相应方法的操作,推断data中写入的int值是否为0,不为0说明之前的p不为null。而且已经存入到data中。调用Person中的CREATOR的createFromParcel()方法,咦。这里又用到了Parcelable的东西。看一下这种方法:

    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];
        }
    };
    protected Person(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    能够看到,系统在这里创建了一个Person实例。传入了我们之前的data(保存有person信息),并将data中的信息赋值给新的person对象。

    回到Stub类的onTransace()方法中继续往下看:

    java.lang.String _result = this.inPerson(_arg0);
    reply.writeNoException();
    reply.writeString(_result);
    return true;

    接着系统调用了this.inPerson(_arg0)方法。_arg0为上边通过createFromParcel()方法新生成的person(包括传递过来的person值)对象,我们知道Stub为抽象类。他实现了我们外层的Interface类。可是没有实现inPerson()方法。那么我们就须要它的子类实现,而我们的mRemote.transace()中的mRemote就是服务端返回的Stub的一个实现子类,也就是说这里调用的this.inPerson(_arg0)方法实际是调用了服务端的那个Stub实现类里边的inPerson()方法,看一下我们之前服务端的inPerson()方法的详细实现:

    @Override
    public String inPerson(Person p) throws RemoteException {
        String old = "name:" + p.getName() + " age:" + p.getAge();
        Log.d("Log_LYL:iPerson_", old);
        p.setName("李四");
        p.setAge(13);
        return "name:" + p.getName() + " age:" + p.getAge();
    }

    通过以上分析,大家就知道了这里的 Log.d("Log_LYL:iPerson_", old); 是为什么有值了,由于我们一開始就将传入的person值附到了Parcel类型的data中了,然后又通过createFromParcel()方法将data中的值付给了新的person对象。

    然后回到Stub类的onTransace()方法,会看到调用了reply.writeNoException();reply.writeString(_result);两个方法,将服务端处理完后返回的信息写入了reply中。最后return

    再回到Proxy类中的inPerson()方法中:

    mRemote.transact(Stub.TRANSACTION_inPerson, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readString();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;

    将服务端返回的信息读出来并付给_result。最后返回,这样我们在activity中就能够得到服务端处理后的结果了。到如今为止。我们就应该明确了,通过in修饰符传递person后服务端生成了新的person对象。并对新person对象进行处理,这种话不管服务端怎样改动person对象我们client的person对象是不会受不论什么影响的,为什么,由于不是一个对象。这就是in操作符。

    以下我们分析一下out操作符,直接看代码:

    String outPerson(out Person p);//.aidl文件里定义;
    mService.outPerson(p2)//client调用;

    我们在client直接通过绑定时返回的Stub实例调用outPerson()方法。通过上边对inPerson()方法的介绍我们知道不是同一个进程中的会终于调用到Proxy中对应的方法中,那我们直接看Proxy中代码:

    @Override
    public java.lang.String outPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_outPerson, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readString();
            if ((0 != _reply.readInt())) {
                p.readFromParcel(_reply);
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    有了上边对inPerson()方法的了解这个相对就简单多了,相同生成两个Parcel对象。作用相同,不同的是用out修饰后没有对传入的pnull推断,也没有取值保存到data中。而是直接调用了mRemote.transact(Stub.TRANSACTION_outPerson, _data, _reply, 0)方法,那好,通过前边我们直接去Stub中找onTransact()方法吧:

    case TRANSACTION_outPerson: {
        data.enforceInterface(DESCRIPTOR);
        com.jason.aidl.aidldemo.Person _arg0;
        _arg0 = new com.jason.aidl.aidldemo.Person();
        java.lang.String _result = this.outPerson(_arg0);
        reply.writeNoException();
        reply.writeString(_result);
        if ((_arg0 != null)) {
            reply.writeInt(1);
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            reply.writeInt(0);
        }
        return true;
    }

    我们仅仅要out方法相关信息,这里系统通过new Person()的方式创建了一个person对象实例赋值个_arg0,这个对象与我们调用时传进来的对象没有一点关系,然后直接调用了this.outPerson()方法,通过inPerson()的介绍我们就知道这里直接调用了服务端outPerson()实现:

    @Override
    public String outPerson(Person p) throws RemoteException {
        //这里的p是空的,由于在IMyAidlInterface的Stub类中onTransact()方法中没有写入;
        // _arg0 = new com.jason.aidl.aidldemo.Person();
        //java.lang.String _result = this.outPerson(_arg0);
        String old = "name:" + p.getName() + " age:" + p.getAge();
        Log.d("Log_LYL:outPerson_", old);
        p.setName("周六");
        p.setAge(20);
        return "name:" + p.getName() + " age:" + p.getAge();
    }

    这里能够知道old是没有赋值的,以下赋值return,回到Stub中:

    java.lang.String _result = this.outPerson(_arg0);
    reply.writeNoException();
    reply.writeString(_result);
    if ((_arg0 != null)) {
        reply.writeInt(1);
        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
    } else {
        reply.writeInt(0);
    }
    return true;

    将处理结果写入reply中。推断_arg0是否为null,不是,写入1,这里重新用到了writeToParcel()方法,通过上边inPerson()方法介绍可知这里是将_arg0的值付给reply。假设为null,reply中赋值0。回到Proxy中:

    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        mRemote.transact(Stub.TRANSACTION_outPerson, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readString();
        if ((0 != _reply.readInt())) {
            p.readFromParcel(_reply);
        }
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;

    运行完transact()方法后从_reply中读出服务端操作返回结果,推断写入的int值,假设不为0,说明_reply中写入了服务端产生的person信息,则将通过readFromParcel()方法将新的_reply中保存的服务端的person信息写入到client传入的p中,这样client就收到了服务端的person信息。

    总结一下out修饰符。即使我们在服务端将p的值写好。服务端也不会接收我们的client信息。能够说服务端根本不关系client传入的信息,服务端通过new Person()方式产生新对象,并返回给client,client的p通过readFromParcel()方法获得服务端值,并没有做对象的交换。

    下边看一下inout修饰符代码:

    String inOutPerson(inout Person p);//.aidl文件里;
    mService.inOutPerson(p3)。//client调用;

    相同看一下Proxy中对应方法:

    @Override
    public java.lang.String inOutPerson(com.jason.aidl.aidldemo.Person p) throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            if ((p != null)) {
                _data.writeInt(1);
                p.writeToParcel(_data, 0);
            } else {
                _data.writeInt(0);
            }
            mRemote.transact(Stub.TRANSACTION_inOutPerson, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readString();
            if ((0 != _reply.readInt())) {
                p.readFromParcel(_reply);
            }
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    case TRANSACTION_inOutPerson: {
        data.enforceInterface(DESCRIPTOR);
        com.jason.aidl.aidldemo.Person _arg0;
        if ((0 != data.readInt())) {
            _arg0 = com.jason.aidl.aidldemo.Person.CREATOR.createFromParcel(data);
        } else {
            _arg0 = null;
        }
        java.lang.String _result = this.inOutPerson(_arg0);
        reply.writeNoException();
        reply.writeString(_result);
        if ((_arg0 != null)) {
            reply.writeInt(1);
            _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            reply.writeInt(0);
        }
        return true;
    }

    能够看到在inout修饰符时推断了传入的p对象,也就是说服务端接收了保存了client的p对象,操作类似与in修饰符,也是通过Person.CREATOR.createFromParcel(data)创建新对象,在Proxy中相同推断了_reply中写入的int值,也就是说,client也能收到服务端对对象的改动,也就是说。inout操作符是int、out操作符的合体。到这里相信大家已经明确in、out、inout操作符的差别了。而且连实现都明确了。

       到如今今天要跟大家分享的东西就写完了,相信您看完这篇文章会对Android中的Aidl有一个新的了解。对inoutinout修饰也明确了,对实现Parcelable接口有了新的认识,假设是这样,那我这篇文章就没白写。谢谢大家。



    转载于:https://www.cnblogs.com/blfbuaa/p/7400678.html

    展开全文
  • 前言: 最近在看VirtualApk的源码,发现里面很多涉及到代理模式 ...Tips:此文章不讲述Binder底层的原理,只是通过AIDL调用的分析,来借花献佛 AIDL Android Interface Definition Language ...

    前言:

    最近在看VirtualApk的源码,发现里面很多涉及到代理模式

    Binder调用本身就是使用一个超级复杂的代理模式

    而我们接触到最多的Binder调用就是AIDL

    因此这里分析一下AIDL的调用原理

    Tips:此文章不讲述Binder底层的原理,只是通过AIDL调用的分析,来借花献佛

     

    AIDL

    Android Interface Definition Language

    Android 接口定义语言

    是android提供的一种本地调用远程服务接口的方式

     

    先来看一下如何使用AIDL

    首先定义AIDL接口

    package vivo.testaidl;

     

    interface IAidlInterface {

           int addition(int a,int b);

    }

    编译一下

    服务端代码编写

    public class MyAidlService extends Service {

        /**

         * 创建生成的本地 Binder 对象,实现 AIDL 制定的方法

         */

        private IBinder mIBinder = new IAidlInterface.Stub() {

            @Override

            public int addition(a,b) throws RemoteException {

                return a + b;

            }

        };

     

        /**

         * 客户端与服务端绑定时的回调,返回 mIBinder 后客户端就可以通过它远程调用服务端的方法,即实现了通讯

         * @param intent

         * @return

         */

        @Nullable

        @Override

        public IBinder onBind(Intent intent) {

            return mIBinder;

        }

    }

    客户端编写

    private IAidlInterface mAidl;

     

    private ServiceConnection mConnection = new ServiceConnection() {

     

        @Override

        public void onServiceConnected(ComponentName name, IBinder service) {

            //连接后拿到 Binder,转换成 AIDL,在不同进程会返回个代理

            mAidl = IAidlInterface.Stub.asInterface(service);

            int result = addition.

        }

     

        @Override

        public void onServiceDisconnected(ComponentName name) {

            mAidl = null;

        }

    };

     

    Intent intent1 = new Intent(getApplicationContext(), MyAidlService.class);

    bindService(intent1, mConnection, BIND_AUTO_CREATE);

    看上去很简单,AIDL对内部的一些处理做了封装,那么要了解AIDL运作的整个过程,我们不妨看一下AS根据AIDL文件生成的java源码

    这个java源码就在generatedJava文件夹下

    如图所示

     

    生成的源码格式化后如下

    /*

     * This file is auto-generated.  DO NOT MODIFY.

     * Original file: C:\\WorkTool\\vivo_workspace\\Collections\\aidl\\src\\main\\aidl\\vivo\\testaidl\\IAidlInterface.aidl

     */

    package vivo.testaidl;

     

    public interface IAidlInterface extends android.os.IInterface {

        /**

         * Local-side IPC implementation stub class.

         */

        public static abstract class Stub extends android.os.Binder implements vivo.testaidl.IAidlInterface {

            private static final java.lang.String DESCRIPTOR = "vivo.testaidl.IAidlInterface";

     

            /**

             * Construct the stub at attach it to the interface.

             */

            public Stub() {

                this.attachInterface(this, DESCRIPTOR);

            }

     

            /**

             * Cast an IBinder object into an vivo.testaidl.IAidlInterface interface,

             * generating a proxy if needed.

             */

            public static vivo.testaidl.IAidlInterface asInterface(android.os.IBinder obj) {

                if ((obj == null)) {

                    return null;

                }

                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

                if (((iin != null) && (iin instanceof vivo.testaidl.IAidlInterface))) {

                    return ((vivo.testaidl.IAidlInterface) iin);

                }

                return new vivo.testaidl.IAidlInterface.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_addition: {

                        data.enforceInterface(DESCRIPTOR);

                        int _arg0;

                        _arg0 = data.readInt();

                        int _arg1;

                        _arg1 = data.readInt();

                        int _result = this.addition(_arg0, _arg1);

                        reply.writeNoException();

                        reply.writeInt(_result);

                        return true;

                    }

                }

                return super.onTransact(code, data, reply, flags);

            }

     

            private static class Proxy implements vivo.testaidl.IAidlInterface {

                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 int addition(int a, int b) throws android.os.RemoteException {

                    android.os.Parcel _data = android.os.Parcel.obtain();

                    android.os.Parcel _reply = android.os.Parcel.obtain();

                    int _result;

                    try {

                        _data.writeInterfaceToken(DESCRIPTOR);

                        _data.writeInt(a);

                        _data.writeInt(b);

                        mRemote.transact(Stub.TRANSACTION_addition, _data, _reply, 0);

                        _reply.readException();

                        _result = _reply.readInt();

                    finally {

                        _reply.recycle();

                        _data.recycle();

                    }

                    return _result;

                }

            }

     

            static final int TRANSACTION_addition = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

        }

     

        public int addition(int a, int b) throws android.os.RemoteException;

    }

    这一个个内部类继承又嵌套的看上去好难受,先不说google为啥这么写,拆分开再说

    generatedJava中Stub继承自Binder 又实现了IAidlInterface接口,为了分离Stub和IAidlInterface 我们将其拆开,并在Stub初始化的时候传入一个实现了addition方法的IAidlInterface对象

    asInterface()因为是一个静态公共方法,其实可以看成是一个工具方法,我们将其放在独立的Utils类下

    因此我们将其拆分成4个类

    1:AidlInterfaceBinderStub.java

     

    下面请看源码注释

    package vivo.testaidl;

     

    /**

     * Created by chenlong on 2019/1/31.

     */

     

    import android.util.Log;

     

    /**

     * Stub 的几个关键内容介绍:

     * 构造函数

     * 调用了 attachInterface() 方法

     * 将一个描述符、特定的 IAidlInterface  与当前 Binder 绑定起来,这样后续调用 queryLocalInterface 就可以拿到这个

     * 这里使用一个 DESCRIPTOR,一般是类的具体路径名,来做key ,用于唯一表示这个 IAidlInterface

     * onTransact()

     * Binder 关键的处理事物方法

     * 根据传入的 code,调用本地/服务端的不同方法

     * AIDL是一个典型的远程代理模式

     */

    public class AidlInterfaceBinderStub extends android.os.Binder {

     

        /**

         * 我们知道在跨进程通信中Binder对象是携带着IAidlInterface所定义的功能的

         * 但是Binder通信机制中那么多Binder都有Interface。那么系统怎么识别哪个Binder是哪个Binder呢?

         * 所以IAidlInterface只是一个能力的抽象,DESCRIPTOR就是来表示具体是哪一个功能Interface。

         */

        public static final String DESCRIPTOR = "vivo.testaidl.IAidlInterface";

     

        public static final String TAG = "TAG_IAidlInterface";

     

        private IAidlInterface mAidlInterface = null;

     

        /**

         * Construct the stub at attach it to the interface.

         * 在Binder内部保存mAidlInterface作为自己的owner

         * 和这个功能更对应的唯一描述descriptor,方便在通信的时候寻找。

         */

        public AidlInterfaceBinderStub(IAidlInterface mAidlInterface) {

            Log.d(AidlInterfaceBinderStub.TAG, "AidlInterfaceBinderStub---init  AidlInterfaceBinderStub = " this " mAidlInterface = " + mAidlInterface);

            this.mAidlInterface = mAidlInterface;

            this.attachInterface(mAidlInterface, DESCRIPTOR);

        }

     

        @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: {

                    Log.d(AidlInterfaceBinderStub.TAG, "AidlInterfaceBinderStub---onTransact---   INTERFACE_TRANSACTION---" this);

                    reply.writeString(DESCRIPTOR);

                    return true;

                }

                case TRANSACTION_addition: {

                    Log.d(AidlInterfaceBinderStub.TAG, "AidlInterfaceBinderStub---onTransact---   TRANSACTION_addition----" this);

                    data.enforceInterface(DESCRIPTOR);

                    int _arg0;

                    _arg0 = data.readInt();

                    int _arg1;

                    _arg1 = data.readInt();

                    // 调用 mAidlInterface的addition 方法,这个方法的实现是在服务端是 mAidlInterface服务端new出来的

                    int _result = mAidlInterface.addition(_arg0, _arg1);

                    // 得出方法返回值之后写写入序列化数据

                    reply.writeNoException();

                    reply.writeInt(_result);

                    return true;

                }

            }

            return super.onTransact(code, data, reply, flags);

        }

     

        static final int TRANSACTION_addition = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

    }

    2 : AidlInterfaceProxy.java

    package vivo.testaidl;

     

    import android.util.Log;

     

    /**

     * Created by chenlong on 2019/1/31.

     * AidlInterfaceProxy是一个使用代理模式实现的interface代理,当通讯双方不在一个进程时,IAidlInterface的方法传入的参数是需要做序列化的,此代理类就是代理实现序列化,并在完成序列化后之后

     * 调用传入的远端Binder的transact方法,远端binder的transact方法在执行之后,就会被代理执行到AidlInterfaceBinderStub中的onTransact实现参数的反序列化,方法执行,写入序列化结果,并最终在AidlInterfaceProxy的addition中返回

     */

    public class AidlInterfaceProxy implements IAidlInterface {

     

        /**

         * 这里的mRemote其实就是其他进程传过来的BinderProxy

         * 代理的远端 IBinder

         */

        private android.os.IBinder mRemote;

     

        AidlInterfaceProxy(android.os.IBinder remote) {

            Log.d(AidlInterfaceBinderStub.TAG,"MyAidlInterfaceProxy---init");

            mRemote = remote;

        }

     

        @Override

        public android.os.IBinder asBinder() {

            return mRemote;

        }

     

        public String getInterfaceDescriptor() {

            return AidlInterfaceBinderStub.DESCRIPTOR;

        }

     

        @Override

        public int addition(int a, int b) throws android.os.RemoteException {

            Log.d(AidlInterfaceBinderStub.TAG,"MyAidlInterfaceProxy---addition");

            android.os.Parcel _data = android.os.Parcel.obtain();

            android.os.Parcel _reply = android.os.Parcel.obtain();

            int _result;

            try {

                _data.writeInterfaceToken(AidlInterfaceBinderStub.DESCRIPTOR);

                _data.writeInt(a);

                _data.writeInt(b);

                // 调用BinderProxy的transact方法

                // BinderProxy是通过transactNative来与远程Binder跨进程通信的

                mRemote.transact(AidlInterfaceBinderStub.TRANSACTION_addition, _data, _reply, 0);

                // 这里最终完成了远程调用 返回数据已经被序列化写入 , 下面就是读出来就行了

                _reply.readException();

                _result = _reply.readInt();

            finally {

                _reply.recycle();

                _data.recycle();

            }

            return _result;

        }

    }

    3 : AidlInterfaceUtils.java

    package vivo.testaidl;

     

    import android.util.Log;

     

    /**

     * Created by chenlong on 2019/1/31.

     */

     

    public class AidlInterfaceUtils {

        /**

         * Cast an IBinder object into an vivo.testaidl.IMyAidlInterface interface,

         * generating a proxy if needed.

         * 接收一个IBinder(这个IBinder是系统传入的), 把这个IBinder转化为它所具有功能接口

         * asInterface方法的作用是识别当前client与service是否在同一个进程,参数obj其实是onServiceConnected时传入的远端IBinder

         * 这里要注意一下 分两种情况

         * 1:当client和service在同一进程下时,onServiceConnected传入的binder就是service中onBind传入的binder对象,此时

         * asInterface在进行检测后(queryLocalInterface)会直接返回这个binder中的IMyAidlInterface对象,并调用IMyAidlInterface中定义的方法,此为本地调用,不经过ipc机制,效率高

         * 2:当client和service不在同一进程下时,onServiceConnected传入的binder是一个代理binder名叫BinderProxy

         * asInterface进行检测此BinderProxy(queryLocalInterface)并不包含IMyAidlInterface对象,于是返回一个IMyAidlInterface的代理对象MyAidlInterfaceBinderStub

         * 当client调用代理对象MyAidlInterfaceProxy的addition方法时会addition方法传入的参数进行序列化操作,然后调用BinderProxy的transact方法,

         * transact方法经过底层后会调用service中onBind传入的binder对象(MyAidlInterfaceStub)的onTransact,onTransact中实现序列化和反序列化,并最终调用MyAidlInterfaceStub

         * 中IMyAidlInterface的addition方法,MyAidlInterfaceBinderStub中的IMyAidlInterface是在service中实现的

         */

        public static IAidlInterface asInterface(android.os.IBinder obj) {

            Log.d(AidlInterfaceBinderStub.TAG, "AidlInterfaceUtils---asInterface---" obj.getClass().getName());

            if ((obj == null)) {

                return null;

            }

            //即如果参数descriptor和这个Binder的功能唯一描述相同。就会返回Binder的功能mOwner。

            //这里是同进程下的通讯queryLocalInterface会返回owner

            android.os.IInterface iin = obj.queryLocalInterface(AidlInterfaceBinderStub.DESCRIPTOR);

            if (((iin != null) && (iin instanceof IAidlInterface))) {

                return ((IAidlInterface) iin);

            }

            //调用者和Binder对象不在同一个进程 这时系统实际传的是一个BinderProxy

            //你可以理解为它是另一个进程中的Binder的替身。我们就可以把它当成另一个进程的Binder。

            // BinderProxy的queryLocalInterface()方法返回的是null

            //不同进程下返回的是一个Proxy

            return new AidlInterfaceProxy(obj);

        }

    }

    4 : IAidlInterface.java

    /*

     * This file is auto-generated.  DO NOT MODIFY.

     * Original file: D:\\WorkTool\\vivo_workspace_demo\\TestAIDL\\app\\src\\main\\aidl\\vivo\\testaidl\\IMyAidlInterface.aidl

     */

    package vivo.testaidl;

     

    public interface IAidlInterface extends android.os.IInterface {

        int addition(int a, int b) throws android.os.RemoteException;

    }

     

    以上四个类就是全部AIDL的逻辑流程,是不是发现其实以后不需要写AIDL文件,只要实现以上四个类就可以完成一个自定义的binder远程调用了。

    下面再画个图总结下

     

    AIDL调用

    在各方面都体现着代理模式的思想,了解并清楚了这个流程,对分析系统AMS有很大的帮助

     

    上述内容有一部分来自于其他大神的文章摘抄,本用于自己学习记录所用,由于之前写的比较仓促没有记下网址,此处先表示感谢,如有朋友看到类似的的文段,实属必然,后面我会做好记录。

     

    附code

    https://download.csdn.net/download/binghelonglong123/11013011

    展开全文
  • 1,server端:新建aidl文件IMyInterface,在aidl文件中声明功能方法。 2,执行 build ->make project 3,新建ServerService继承Service,在onBind方法中实现 IMyInterface.stub并返回: public IBinder onBind(Intent...
  • AOSP新增AIDL调用

    2018-12-11 11:02:42
    在做AOSP开发的时候可能会遇到这种情况,上层的APP需要调用Api做一些操作,但是这些操作App是没有权限去做的,只有系统进程才有权限去做. 该怎么样实现呢, 其实很简单,仿照其它的Manager去做即可.  1. 写一个AIDL接口...
  • 一、AIDL代码详细解析 /* * This file is auto-generated.... * Original file: E:\\android_projects\\Android-PickerView-master\\AidlDemo\\app\\src\\main\\aidl\\com\\suning\\aidldemo\\IAddInterfac...
  • AIDL开发流程

    2016-09-14 14:56:50
    AIDL有自己的语法规则,并且AIDL支持的语法类型是有限的: 基本类型 String、CharSequece AttayList(其中的元素能够被AIDL支持) HashMap(其中的元素能够被AIDL支持) Parcelable AIDL 2、用...
  • AIDL的全称是Android Interface definition language,一看就明白,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间的通信接口,用处当然就是用来进程间的通信和方法调用了。其实AIDL是Binder...
  • android studio使用Aidl跨进程调用服务
  • Android AIDL流程学习

    千次阅读 2017-10-23 09:16:59
    前言简介快速上手 服务端 创建载体MessageBean创建AIDL文件MessageBeanAIDL创建AIDL...拷贝AIDL文件夹开启服务关联对象调用方法 附加技能定时推送消息 服务端项目推送消息客户端项目接收定时推送 结束语项目下载
  • AIDL:Android Interface Definition Language,即 Android 接口...为了使其他的应用程序也可以访问本应用程序提供的服务,Android 系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的...
  • AIDL全称Android Interface Definition Language,一种android的接口定义语言,用于进程间通讯,我们知道android是不允许不同进程间直接共享数据的,但是有几种解决办法,比如ContentProvider,AIDL等等,那么什么...
  • AIDL的用途不多介绍了,我写了个liz
  • android AIDL

    2017-08-17 17:15:01
    AIDL(Android Interface Definition Language)是一种IDL语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程...
  • 使用AIDL实现Android的跨进程通信

    千次阅读 2016-06-30 18:09:06
    AIDL(Android Interface Definition Language), 即Android接口定义语言. 在Android中, AIDL是跨进程通信的主要实现方式. 我们同样也可以使用AIDL, 实现自己的跨进程方案. 本文介绍AIDL的使用方式.服务端: 创建...
  • 意义:由于每个应用进程都有自己的独立进程空间,在android平台上,一个进程...定义:AIDL(Android Interface Definition Language)是一种IDL语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interpro...
  • AIDLAndroid跨进程通信

    千次阅读 2016-07-23 17:31:26
    AIDLAndroid实现IPC的一种重要的方式,理解它的原理对理解Android进程间通信有很大的帮助。AIDL的定义,已经有很多介绍的文章了,这里就不做详解了。我们直接从实例入手来分析AIDL实现原理。  AIDL的使用  ...
  • 服务端定义AIDL接口:例如:A.aidl,它的位置也在service所在目录,ADT会在该目录下生成一个A.java接口,接口里包含了一个stub内部类,其实现了A和IBinder(普通Service被调用时都要通过它来回调以传输数据)两个...
  • Android AIDL

    2017-12-28 17:10:07
    Android AIDL】 简介 A [android] I [Interface] D [Definition] L [Language] Android接口定义语言。 作用:方便系统为我们生成代码从而实现跨进程通讯,也就是说这个AIDL就只是一个快速跨进程通讯的...
  • 2)实现接口3)公开接口4)实现客户端5)支持自定义类型6)服务端通知客户端3、aidl自动生成的java代码4、aidl流程 1、简介 AIDL是一种接口描述语言,用于进程间通信,有如下几个特点。 1)AIDL用于多个客户端跨进程...
  • ——服务端—— 当Service与外部通信时,需要传递复杂数据,即Stub子类中的...2.它们都要各自定义AIDL. 例如:A.aidl:parcelable A;B.aidl:parcelable B;因为它们的数据其实都在各自java类中,所以它们的AIDL定义
  • 概述 如果使用aidl来进行IPC,在client进程执行的transct方法后最终会执行到server进程的onTransact方法,如下图: 对aidl还不是很了解的读者可以看下笔者的前文: ...调用流程 先给出流程图,便于读者有一个整体
  • FM AIDL JNI 调用过程回顾 FMPlay.java public class FMPlay extends Activity  implements ServiceConnection;   private RadioServiceStub mService; public void onCrea
  • Android的硬件抽象层: 简单来说,就是对Linux内核驱动程序的封装,向上提供接口,屏蔽低层的实现细节。 也就是说,把对硬件的支持分成了两层, 一层放在用户空间(User Space),(硬件抽象层) 一层放在内核空间(Ker

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,098
精华内容 4,039
关键字:

aidl调用流程android