Android AIDL 原理解析

隐身守侯 提交于 2020-11-03 03:29:01

Android AIDL 原理解析

如果去阅读Android的源代码,就会发现里面大量用到了Binder、AIDL相关知识,比如当我们去使用AMSPMSWMS这些核心服务,因为他们都运行在 system_server 进程,普通应用想调用他们提供的服务(例如:startActivity(),就需要AMS来实现),就必须要跨进程调用,因此,我们在阅读代码之前,必须先去尝试理解Binder、AIDL相关知识。

为什么要使用AIDL呢?

通过AIDL,可以让本地调用远程服务器的接口就像调用本地接口那么简单,让用户无需关注内部细节,只需要实现自己的业务逻辑接口,内部复杂的参数序列化发送、接收、客户端调用服务端的逻辑,你都不需要去关心了。

一 从一个例子开始

我们通过一个简单的跨进程调用的例子来理解AIDL。

设计一个简单的场景:

我们有一个CoreService运行在":core"进程中,提供文件下载服务,我们会在Activity中去bind这个Service,并且调用它为我们提供的服务。

实现起来很简单,只需要以下几个步骤:

  • 定义一个AIDL文件
 interface IDownloadService {
 
    /**
     * 下载
     * @param url
     */
     void download(String url);

     /**
     * 删除下载任务
     * @param url
     */
     void delete(String url);

     /**
     * 停止下载任务
     * @param url
     */
     void stop(String url);

     /**
     * 获取下载队列大小
     * @return
     */
     int getQueueSize();
}
  • 通过AIDL的代码生成器,生成实现Proxy、Stub代码逻辑 以Android Studio为例,自动生成的IDownloadService.java位于\app\build\generated\source\aidl\debug目录下,核心方法我已添加注释。
public interface IDownloadService extends android.os.IInterface {
    /**
     * 下载
     *
     * @param url
     */
    public void download(java.lang.String url) throws android.os.RemoteException;

    /**
     * 删除下载任务
     *
     * @param url
     */
    public void delete(java.lang.String url) throws android.os.RemoteException;

    /**
     * 停止下载任务
     *
     * @param url
     */
    public void stop(java.lang.String url) throws android.os.RemoteException;

    /**
     * 获取下载队列大小
     *
     * @return
     */
    public int getQueueSize() throws android.os.RemoteException;

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.cundong.touch.IDownloadService {
    
        // 注意,通过以下代码我们发现,我们在.aidl中定义的方法名字其实没什么意义,服务器端根本没有使         // 用这些名字,而是自动为这些方法生成了递增的ID,根据方法的顺序而不是名字来一个一个处理
        static final int TRANSACTION_download = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_delete = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_stop = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_getQueueSize = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        private static final java.lang.String DESCRIPTOR = "com.cundong.touch.IDownloadService";

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

        /**
         * Cast an IBinder object into an com.cundong.touch.IDownloadService interface,
         * generating a proxy if needed.
         * 
         * 注:
         * 这个方法里面有个处理,通过queryLocalInterface查询,如果服务端和客户端都是在同一个进程,那          * 么就不需要跨进程了,直接将IDownloadService当做普通的对象来使用即可,否则返回远程对象
         * 的代理
         */
        public static com.cundong.touch.IDownloadService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.cundong.touch.IDownloadService))) {
                return ((com.cundong.touch.IDownloadService) iin);
            }
            return new com.cundong.touch.IDownloadService.Stub.Proxy(obj);
        }
        
        /**
        * 返回Binder实例
        */
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
        * 根据code参数来处理,这里面会调用真正的业务实现类
        */
        @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_download: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.download(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_delete: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.delete(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_stop: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    this.stop(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getQueueSize: {
                    data.enforceInterface(DESCRIPTOR);
                    int _result = this.getQueueSize();
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.cundong.touch.IDownloadService {
            private android.os.IBinder mRemote;
            
            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
            
            //Proxy的asBinder()返回位于本地接口的远程代理
            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }
            
            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * 下载
             *
             * @param url
             */
            @Override
            public void download(java.lang.String url) 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.writeString(url);
                    
                    //Proxy中, 业务接口中其实没有做任何业务逻辑处理,仅仅是收集本地参数,序列化
                    //后通过IBinder的transact方法,发给服务器端,并且通过_reply来接收服务器端
                    //的处理结果
                    mRemote.transact(Stub.TRANSACTION_download, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            /**
             * 删除下载任务
             *
             * @param url
             */
            @Override
            public void delete(java.lang.String url) 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.writeString(url);
                    mRemote.transact(Stub.TRANSACTION_delete, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            /**
             * 停止下载任务
             *
             * @param url
             */
            @Override
            public void stop(java.lang.String url) 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.writeString(url);
                    mRemote.transact(Stub.TRANSACTION_stop, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            /**
             * 获取下载队列大小
             *
             * @return
             */
            @Override
            public int getQueueSize() 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);
                    mRemote.transact(Stub.TRANSACTION_getQueueSize, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
    }
}

  • 继承IDownloadService.Stub,实现真正的服务器端接口
public class IDowanloadServiceImpl extends IDownloadService.Stub {
    private final Random mGenerator = new Random();

    @Override
    public void download(String url) throws RemoteException {
        //TODO 真正的业务逻辑
    }

    @Override
    public void delete(String url) throws RemoteException {
        //TODO 真正的业务逻辑
    }

    @Override
    public void stop(String url) throws RemoteException {
        //TODO 真正的业务逻辑
    }

    @Override
    public int getQueueSize() throws RemoteException {
        //TODO 真正的业务逻辑
        return mGenerator.nextInt(100);
    }
}
  • 远程Service的onBind()接口返回Stub的IBinder
public class CoreService extends Service {

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new IDowanloadServiceImpl();
    }
}
  • 客户端发起远程调用
private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {

            sIDownloadService = IDownloadService.Stub.asInterface(service);
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
//调用
if (mBound) {
        int size = 0;
        try {
           size = sIDownloadService.getQueueSize();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
 }

二 原理分析

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

1. UML图

IDownloadService.java UML图

2. 代码分析

代码主要分为Proxy、Stub两部分。

1) Proxy

Proxy运行在客户端,它实现了IDownloadService接口,并且持有一个远程代理IBinder mRemote,mRemote不做任何业务逻辑处理,仅仅通过IBinder接口的transact()方法,把客户端的调用参数序列化后transact到远程服务器。

示例:

@Override
public void download(java.lang.String url) 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.writeString(url);
                mRemote.transact(Stub.TRANSACTION_download, _data, _reply, 0);
                _reply.readException();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
}         

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

2) Stub

Stub运行在服务器端,继承自Binder,同样也实现了IDownloadService接口,它的核心逻辑在onTransact方法中:

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    switch (code) {
        case TRANSACTION_download: {
                data.enforceInterface(DESCRIPTOR);
                java.lang.String _arg0;
                _arg0 = data.readString();
                this.download(_arg0);
                reply.writeNoException();
                return true;
        }
        case TRANSACTION_delete: {
            ...
        }
        case TRANSACTION_stop: {
            ...
        }
        case TRANSACTION_getQueueSize: {
            ...
        }
    }
    return super.onTransact(code, data, reply, flags);
}

远程服务器端通过IBinder接口的onTtransact()方法来接收数据、处理数据,并且调用真实的业务逻辑代码(如上面的download接口),通过reply像客户端传递返回值。

另外,Stub中另外一个比较重要的接口就是asInterface()接口,我们在客户端真正使用的时候通常会这样使用它:

IDownloadService sIDownloadService = IDownloadService.Stub.asInterface(IBinder service);

通过方法名字,我们大致可以猜出,它大概实现的功能,就是将一个IBinder对象转化为接口对象IDownloadService,通过代码可以看到具体逻辑:

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

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

至此,我们就通过源码层面了解了AIDL是如何工作了。

参考:

[1][http://www.jianshu.com/p/cfb1d2a109a2]

[2][http://blog.csdn.net/xude1985/article/details/9232049]

[3][http://developer.android.com/intl/zh-cn/guide/components/aidl.html]

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!