AIDL学习(二)---代码分析

之前已经学习过AIDL的用法和一些需要注意的地方,现在来关注一下AIDL的内部实现过程。

之前只是定义了一个.aidl文件就能让我们进行跨进程通讯了,这实在是很爽,但是还是要去了解一下,系统帮我们做了什么工作。

  • 先看一下.aidl对应的.java的内部结构

package com.charles_lun.db.aidl;

interface IRemoteService{

    int getPid();
    //参数默认是in
    void basicTypes(int anInt,long aLong,boolean aBoolean,
                    float aFloat,double aDouble,String aString);

}

aidl.png-32.2kB
很清晰的能看到,接口IRemoteService中包含一个静态抽象类Stub.class,还有一个静态代理类Proxy.class,以及两个抽象方法getPid(),basicTypes();

再来看下简单的UML图,这看着是不是似曾相识,这就是代理模式吧。
IMG_20161202_172627.jpg-2359.1kB

理清各个接口,类之间的关系,对下面分析代码很有帮助,有助于对逻辑的理解。

AIDL的代码生成器,已经根据.aidl文件自动帮我们生成Proxy、Stub(抽象类)两个类,并且把客户端代理mRemote的transact()过程以及 服务器端的onTtransact()过程默认实现好了,我们只需要在服务器端继承Stub,实现自己的业务类(在onTtransact()中会调用)。

  • 代码分析
    Proxy运行在客户端,它实现了IRemoteService接口,并且持有一个远程代理IBinder mRemote,mRemote不做任何业务逻辑处理,仅仅通过IBinder接口的transact()方法,把客户端的调用参数序列化后transact到远程服务器。
@Override 
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeInt(anInt);
    _data.writeLong(aLong);
    _data.writeInt(((aBoolean)?(1):(0)));
    _data.writeFloat(aFloat);
    _data.writeDouble(aDouble);
    _data.writeString(aString);
    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
    _reply.readException();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
}

_data为调用接口传入的参数,_reply为调用方法得到的返回值,mRemote.transact(Stub.TRANSACTION_download, _data, _reply, 0);为调用过程。

再来看Stub.class,继承自Binder,实现IRemoteService接口,核心的方法在onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags);

@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_getPid:
        {
            data.enforceInterface(DESCRIPTOR);
            int _result = this.getPid();
            reply.writeNoException();
            reply.writeInt(_result);
            return true;
        }
        case TRANSACTION_basicTypes:
        {
            data.enforceInterface(DESCRIPTOR);
            int _arg0;
            _arg0 = data.readInt();
            long _arg1;
            _arg1 = data.readLong();
            boolean _arg2;
            _arg2 = (0!=data.readInt());
            float _arg3;
            _arg3 = data.readFloat();
            double _arg4;
            _arg4 = data.readDouble();
            java.lang.String _arg5;
            _arg5 = data.readString();
            this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
            reply.writeNoException();
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

远程服务端通过IBinder接口的onTransact()来接受数据,处理数据,并且调用真实的逻辑业务代码(IRemoteService中的方法 ),通过传递返回值,而这个真实的逻辑业务需要我们自己去实现。也就是说在AIDL过程中,我们无需关心数据的处理,传递,只需要关注业务的逻辑实现即可
再来一下,客户端需要调用的方法IRemoteService.Stub.asInterface(IBinder)

public Stub()
{   //绑定Binder和IInterface
    this.attachInterface(this, DESCRIPTOR);
}

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

先通过queryLocalInterface查询,如果服务端和客户端都是在同一个进程,那么就不需要跨进程了,直接将IRemoteService当做普通的对象来使用,否则会返回远程对象的代理对象。

可能有人要问queryLocalInterface(String)里面干的啥,那我们就进去看看呗:

Binder.java
  /**
     * Use information supplied to attachInterface() to return the
     * associated IInterface if it matches the requested
     * descriptor.
     */
    public IInterface queryLocalInterface(String descriptor) {
        if (mDescriptor.equals(descriptor)) {
            return mOwner;
        }
        return null;
    }

       /**
     * Convenience method for associating a specific interface with the Binder.
     * After calling, queryLocalInterface() will be implemented for you
     * to return the given owner IInterface when the corresponding
     * descriptor is requested.
     */
    public void attachInterface(IInterface owner, String descriptor) {
        mOwner = owner;
        mDescriptor = descriptor;
    }

这下很明显了吧,attachInterface()将IInterface接口的实现(这里的this指的是AIDLService这个服务)与Binder绑定在一起,然再通过这个Tag去Binder(这个是客户端传过来的)中查询(这里查询结果的是BinderProxy)。很显然两个查出来的不会是同一个东西,如果不好理解,再看下上面的UML图。

看一下,我做的实验:

public Stub()
{
    System.out.println(" attatch....."+DESCRIPTOR+",this:"+this.toString());
    this.attachInterface(this, DESCRIPTOR);
}

public static com.charles_lun.db.aidl.IRemoteService asInterface(android.os.IBinder obj)
{
    if ((obj==null)) {
    return null;
    }

    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    System.out.println("obj:"+obj.toString()+"is iin null:"+(iin == null)+",instanceof IRemoteService:"+(iin instanceof com.charles_lun.db.aidl.IRemoteService));
    if (((iin!=null)&&(iin instanceof com.charles_lun.db.aidl.IRemoteService))) {
    return ((com.charles_lun.db.aidl.IRemoteService)iin);
    }
    return new com.charles_lun.db.aidl.IRemoteService.Stub.Proxy(obj);
}

这个测试中,其余代码都是一样,只是改变AIDLService的运行进程
先设置为跨进程,看下打印结果,两个IInterface的实现不一样吧

12-02 16:31:53.600: I/System.out(1448): 1448
12-02 16:31:53.689: I/System.out(1470):  attatch.....com.charles_lun.db.aidl.IRemoteService,this:com.charles_lun.db.aidl.AIDLService$1@2b47e7b
12-02 16:31:53.689: I/System.out(1470): on create....
12-02 16:31:53.689: I/System.out(1470): on bind....
12-02 16:31:53.720: I/System.out(1448): connect.....thread:main
12-02 16:31:53.720: I/System.out(1448): obj:android.os.BinderProxy@2046880dis iin null:true,instanceof IRemoteService:false

在看设置为非跨进程,打印结果:这个是一样的

12-02 16:33:50.212: I/System.out(2352): 2352
12-02 16:33:50.234: I/System.out(2352):  attatch.....com.charles_lun.db.aidl.IRemoteService,this:com.charles_lun.db.aidl.AIDLService$1@35cf253f
12-02 16:33:50.234: I/System.out(2352): on create....
12-02 16:33:50.234: I/System.out(2352): on bind....
12-02 16:33:50.270: I/System.out(2352): connect.....thread:main
12-02 16:33:50.271: I/System.out(2352): obj:com.charles_lun.db.aidl.AIDLService$1@35cf253fis iin null:false,instanceof IRemoteService:true

这个没骗人吧,两个都是AIDLService,有数据作对比。

说了那么多有点晕了吧,没事,别晕,放一张流程图,是从别人那里盗来的,是不是有点条理了。鉴于水平有限,如果有不正确或者不当的地方,请大大们指正。
201609210927156.png-159.8kB

猜你喜欢

转载自blog.csdn.net/baidu_17508977/article/details/53437167
今日推荐