文档章节

Android AIDL 原理解析

Cundong
 Cundong
发布于 2016/03/29 20:42
字数 1912
阅读 2473
收藏 44

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]

© 著作权归作者所有

Cundong
粉丝 185
博文 28
码字总数 30973
作品 0
海淀
私信 提问
加载中

评论(1)

酷拉皮卡丘
酷拉皮卡丘
博主手画UML, 真是认真, 赞79!!!
Android Camera进程间通信类总结

《Android Camera架构》 《Android Camera进程间通信类总结》 《Android Camera模块解析之拍照》 《Android Camera模块解析之视频录制》 《Android Camera原理之CameraDeviceCallbacks回调模...

天王盖地虎626
07/21
18
0
安卓跨进程通信之Aidl教程详解(一)

安卓多进程通信主要有四大方案:1,Activity 2,Broadcast Receiver 3,Content Prodiver 4,Service 1,Activity Intent intent = new Intent(); intent.setAction(Intent.ACTION_CALL); inte......

修炼爱情
2017/04/19
0
0
android IPC通信(下)-AIDL

版权声明:转载请标明出处http://blog.csdn.net/selfstudy,对技术感兴趣的同鞋加群544645972一起交流 https://blog.csdn.net/zhaozepeng/article/details/50311611   android IPC通信(上...

Shawn_Dut
2015/12/17
0
0
Android AIDL浅析及异步调用

AIDL:Android Interface Definition Language,即 Android 接口定义语言。 AIDL 是什么 Android 系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。 为了使其...

cspecialy
2018/05/20
0
0
android使用aidl实现进程间通信

在Android中, 每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢? 显然,Java中是不支持跨进程内存共享的。因此要传递对象, 需要把对象解析成操作系统能够理解的...

AlexZhuang
2012/04/20
1K
2

没有更多内容

加载失败,请刷新页面

加载更多

spring cloud

一、从面试题入手 1.1、什么事微服务 1.2、微服务之间如何独立通讯的 1.3、springCloud和Dubbo有哪些区别 1.通信机制:DUbbo基于RPC远程过程调用;微服务cloud基于http restFUL API 1.4、spr...

榴莲黑芝麻糊
13分钟前
2
0
Executor线程池原理与源码解读

线程池为线程生命周期的开销和资源不足问题提供了解决方 案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。 线程实现方式 Thread、Runnable、Callable //实现Runnable接口的...

小强的进阶之路
昨天
6
0
maven 环境隔离

解决问题 即 在 resource 文件夹下面 ,新增对应的资源配置文件夹,对应 开发,测试,生产的不同的配置内容 <resources> <resource> <directory>src/main/resources.${deplo......

之渊
昨天
8
0
详解箭头函数和普通函数的区别以及箭头函数的注意事项、不适用场景

箭头函数是ES6的API,相信很多人都知道,因为其语法上相对于普通函数更简洁,深受大家的喜爱。就是这种我们日常开发中一直在使用的API,大部分同学却对它的了解程度还是不够深... 普通函数和...

OBKoro1
昨天
7
0
轻量级 HTTP(s) 代理 TinyProxy

CentOS 下安装 TinyProxy yum install -y tinyproxy 启动、停止、重启 # 启动service tinyproxy start# 停止service tinyproxy stop# 重启service tinyproxy restart 相关配置 默认...

Anoyi
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部