读完本文你将了解:

记得 2015 年实习面试,笔试题里就有这道题:请介绍下 AIDL。

当时的我是懵逼的,只好老老实实空着。没想到后来面试时面试官大哥嘿嘿一笑说他也没用过这玩意,真是够实诚的。

笔试完查了这个知识点,似懂非懂也没深究。去年看《安卓开发艺术探索》时也学了这部分内容,但是可能当时水平不够,或者只是看起来努力,没有真正理解精髓,没多久就又忘了个七八成。

这次复习,还是老老实实敲出来,总结成文字吧,方便以后回顾。

AIDL 是什么

AIDL(Android 接口定义语言) 是 Android 提供的一种进程间通信 (IPC) 机制。

我们可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。

在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。

编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。

通过这种机制,我们只需要写好 aidl 接口文件,编译时系统会帮我们生成 Binder 接口。

AIDL 支持的数据类型

共 4 种:

  1. Java 的基本数据类型
  2. List 和 Map
    • 元素必须是 AIDL 支持的数据类型
    • Server 端具体的类里则必须是 ArrayList 或者 HashMap
  3. 其他 AIDL 生成的接口
  4. 实现 Parcelable 的实体

AIDL 如何编写

AIDL 的编写主要为以下三部分:

  1. 创建 AIDL

    • 创建要操作的实体类,实现 Parcelable 接口,以便序列化/反序列化
    • 新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射 aidl 文件
    • Make project ,生成 Binder 的 Java 文件
  2. 服务端
    • 创建 Service,在其中创建上面生成的 Binder 对象实例,实现接口定义的方法
    • onBind() 中返回
  3. 客户端
    • 实现 ServiceConnection 接口,在其中拿到 AIDL 类
    • bindService()
    • 调用 AIDL 类中定义好的操作请求

AIDL 实例

下面以实例代码演示一个 AIDL 的编写。

1.创建 AIDL

①创建要操作的实体类,实现 Parcelable 接口,以便序列化/反序列化


package net.sxkeji.shixinandroiddemo2.bean;

import android.os.Parcel;
import android.os.Parcelable;

public class Person implements Parcelable {
    private String mName;

    public Person(String name) {
        mName = name;
    }

    protected Person(Parcel in) {
        mName = in.readString();
    }

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

    @Override
    public int describeContents() {
        return 0;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "mName='" + mName + '\'' +
                '}';
    }
}

实现 Parcelable 接口是为了后序跨进程通信时使用。

关于 Parcelable 可以看我的这篇文章 Android 进阶6:两种序列化方式 Serializable 和 Parcelable

注意 实体类所在的包名。

②新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射 aidl 文件

在 main 文件夹下新建 aidl 文件夹,使用的包名要和 java 文件夹的包名一致:

先创建实体类的映射 aidl 文件,Person.aidl:

// Person.aidl
package net.sxkeji.shixinandroiddemo2.bean;

//还要和声明的实体类在一个包里
parcelable Person;

在其中声明映射的实体类名称与类型

注意,这个 Person.aidl 的包名要和实体类包名一致。

然后创建接口 aidl 文件,IMyAidl.aidl:

// IMyAidl.aidl
package net.sxkeji.shixinandroiddemo2;

// Declare any non-default types here with import statements
import net.sxkeji.shixinandroiddemo2.bean.Person;

interface IMyAidl {
    /**
     * 除了基本数据类型,其他类型的参数都需要标上方向类型:in(输入), out(输出), inout(输入输出)
     */
    void addPerson(in Person person);

    List<Person> getPersonList();
}

在接口 aidl 文件中定义将来要在跨进程进行的操作,上面的接口中定义了两个操作:

  • addPerson: 添加 Person
  • getPersonList:获取 Person 列表

需要注意的是:

  • 非基本类型的数据需要导入,比如上面的 Person,需要导入它的全路径。

    • 这里的 Person 我理解的是 Person.aidl,然后通过 Person.aidl 又找到真正的实体 Person 类。
  • 方法参数中,除了基本数据类型,其他类型的参数都需要标上方向类型
    • in(输入), out(输出), inout(输入输出)

③Make Project ,生成 Binder 的 Java 文件

AIDL 真正的强大之处就在这里,通过简单的定义 aidl 接口,然后编译,就会为我们生成复杂的 Java 文件。

点击 Build -> Make Project,然后等待构建完成。

然后就会在 build/generated/source/aidl/你的 flavor/ 下生成一个 Java 文件:

现在我们有了跨进程 Client 和 Server 的通信媒介,接着就可以编写客户端和服务端代码了。

我们先跑通整个过程,这个文件的内容下篇文章介绍。

2.编写服务端代码

创建 Service,在其中创建上面生成的 Binder 对象实例,实现接口定义的方法;然后在 onBind() 中返回

创建将来要运行在另一个进程的 Service,在其中实现了 AIDL 接口中定义的方法:

public class MyAidlService extends Service {
    private final String TAG = this.getClass().getSimpleName();

    private ArrayList<Person> mPersons;

    /**
     * 创建生成的本地 Binder 对象,实现 AIDL 制定的方法
     */
    private IBinder mIBinder = new IMyAidl.Stub() {

        @Override
        public void addPerson(Person person) throws RemoteException {
            mPersons.add(person);
        }

        @Override
        public List<Person> getPersonList() throws RemoteException {
            return mPersons;
        }
    };

    /**
     * 客户端与服务端绑定时的回调,返回 mIBinder 后客户端就可以通过它远程调用服务端的方法,即实现了通讯
     * @param intent
     * @return
     */
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        mPersons = new ArrayList<>();
        LogUtils.d(TAG, "MyAidlService onBind");
        return mIBinder;
    }
}

上面的代码中,创建的对象是一个 IMyAidl.Stub() ,它是一个 Binder,具体为什么是它我们下篇文章介绍。

别忘记在 Manifest 文件中声明:

<service
    android:name="net.sxkeji.shixinandroiddemo2.service.MyAidlService"
    android:enabled="true"
    android:exported="true"
    android:process=":aidl"/>

服务端实现了接口,在 onBind() 中返回这个 Binder,客户端拿到就可以操作数据了。

3.编写客户端代码

这里我们以一个 Activity 为客户端。

①实现 ServiceConnection 接口,在其中拿到 AIDL 类

private IMyAidl mAidl;

private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //连接后拿到 Binder,转换成 AIDL,在不同进程会返回个代理
        mAidl = IMyAidl.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        mAidl = null;
    }
};

在 Activity 中创建一个服务连接对象,在其中调用 IMyAidl.Stub.asInterface() 方法将 Binder 转为 AIDL 类。

②接着绑定服务

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

要执行 IPC,必须使用 bindService() 将应用绑定到服务上。

注意:

5.0 以后要求显式调用 Service,所以我们无法通过 action 或者 filter 的形式调用 Service,具体内容可以看这篇文章 Android 进阶:Service 的一些细节

③拿到 AIDL 类后,就可以调用 AIDL 类中定义好的操作,进行跨进程请求

@OnClick(R.id.btn_add_person)
public void addPerson() {
    Random random = new Random();
    Person person = new Person("shixin" + random.nextInt(10));

    try {
        mAidl.addPerson(person);
        List<Person> personList = mAidl.getPersonList();
        mTvResult.setText(personList.toString());
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

运行结果

可以看到,Activity 与 另外一个进程的 Service 通信成功了。

总结

这篇文章介绍了 AIDL 的简单编写流程,其中也踩过一些坑,比如文件所在包的路径不统一,绑定服务收不到回调等问题。

到最后虽然跨进程通信成功,但是我们还是有很多疑问的,比如:

  • AIDL 生成的文件内容?
  • 什么是 Binder?
  • 为什么要这么写?

知其然还要知其所以然,这一切都要从 Binder 讲起,且听下一回合介绍。

代码地址

Thanks

《Android 开发艺术探索》

https://developer.android.com/guide/components/aidl.html

http://www.jianshu.com/p/b9b15252b3d6

http://rainbow702.iteye.com/blog/1149790

Android 进阶7:进程通信之 AIDL 的使用的更多相关文章

  1. Android 进阶9:进程通信之 AIDL 解析

    读完本文你将了解: AIDL AIDL 生成文件分析 Stub Proxy AIDL 生成的内容小结 AIDL 的使用回顾 服务端 客户端 小结 手动写个 Binder 首先是定义跨进程接口实现 II ...

  2. 图文详解 Android Binder跨进程通信机制 原理

    图文详解 Android Binder跨进程通信机制 原理 目录 目录 1. Binder到底是什么? 中文即 粘合剂,意思为粘合了两个不同的进程 网上有很多对Binder的定义,但都说不清楚:Bin ...

  3. Android上的进程通信(IPC)机制

    Interprocess Communication Android offers a mechanism for interprocess communication (IPC) using rem ...

  4. Android 进阶13:几种进程通信方式的对比总结

    不花时间打基础,你将会花更多时间解决那些不必要的问题. 读完本文你将了解: RPC 是什么 IDL 是什么 IPC 是什么 Android 几种进程通信方式 如何选择这几种通信方式 Thanks RP ...

  5. Android 进阶8:进程通信之 Binder 机制浅析

    读完本文你将了解: IBinder Binder Binder 通信机制 Binder 驱动 Service Manager Binder 机制跨进程通信流程 Binder 机制的优点 总结 Than ...

  6. 详解 CmProcess 跨进程通信的实现

    CmProcess 是 Android 一个跨进程通信框架,整体代码比较简单,总共 20 多个类,能够很好的便于我们去了解跨进程实现的原理. 个人猜测 CmProcess 也是借鉴了 VirtualA ...

  7. Android 进程通信机制之 AIDL

    什么是 AIDL AIDL 全称 Android Interface Definition Language,即 安卓接口描述语言.听起来很深奥,其实它的本质就是生成进程间通信接口的辅助工具.它的存在 ...

  8. Android——进程通信/ AIDL/Message相关知识总结贴

    Android多进程通信 http://www.apkbus.com/android-83462-1-1.html Android 跨进程通信(一) http://www.apkbus.com/and ...

  9. android跨进程通信(IPC)——AIDL

    转载请标明出处: http://blog.csdn.net/sinat_15877283/article/details/51026711: 本文出自: [温利东的博客] 近期在看 @任玉刚 大神编写 ...

随机推荐

  1. s5_day5作业

    # 1.写函数,用户传入修改的文件名,与要修改的内容,执行函数,完成批量修改操作 # def number_file(file,change_s,change): # import os # with ...

  2. 07 Spring框架 依赖注入(四)基于注解的依赖注入

    前面几节我们都在使用xml进行依赖的注入,但是在实际的开发中我们往往偏爱于使用注解进行依赖注入,因为这样更符合我们人的思维,并且更加快捷,本节就来讲述Spring基于注解的依赖注入: 信息注入注解 @ ...

  3. redis 笔记04 服务器、复制

    服务器 1. 一个命令请求从发送到完成主要包括以下步骤: 1). 客户端将命令请求发送给服务器 2). 服务器读取命令请求,并分析出命令参数 3). 命令执行器根据参数查找命令的实现函数,然后执行实现 ...

  4. Linux静默安装Oracle

    打算在云服务器上装oracle服务,以前DBA美眉都是在图形化界面下安装,这次抓瞎了.赶紧上网查查,静默安装可以解决问题.于是乎赶紧开始部署,过程如下.安装环境:操作系统:CentOS 7内存:11G ...

  5. Apahce 加载模块说明

    LoadModule auth_basic_module modules/mod_auth_basic.so #基本认证模块 LoadModule auth_digest_module modules ...

  6. 在Linux上使用Wine安装轻聊版的QQ的步骤讲解

    准备 Wine 环境 wine 版本要求,越新越好,我用的 1.7.55,目前最新是1.8rc2. 删除或者备份你的 ~/.wine,如果你之前运行过 wine 的话.因为涉及到少量配置,尽量不要让以 ...

  7. BZOJ-5424: 烧桥计划(单调队列)

    BZOJ-5424: 烧桥计划(单调队列) 题目链接 题解: 先考虑最暴力的\(dp\):设\(f[k][i]\)表示搞掉第\(1\sim i\)段,烧了\(k\)段的最小花费,设\(calc(x,y ...

  8. jsp基础知识点——思维导图

    如图 思维导图图片链接 http://www.edrawsoft.cn/viewer/public/s/0b8cd083478732 有道云笔记图片链接 http://note.youdao.com/ ...

  9. SPFA算法 - Bellman-ford算法的进一步优化

    2017-07-27  22:18:11 writer:pprp SPFA算法实质与Bellman-Ford算法的实质一样,每次都要去更新最短路径的估计值. 优化:只有那些在前一遍松弛中改变了距离点的 ...

  10. Pyspider爬虫教程

    Pyspider爬虫教程 一.安装 1. 安装pip (1)准备工作 yum install –y make gcc-c++ python-devel libxml2-devel libxslt-de ...