季春初始,天气返暖,新冠渐去,正值学习好时机。在Android系统中,AIDL一直在Framework和应用层上扮演着很重要的角色,今日且将其原理简单分析。(文2020.03.30)
一、开篇介绍
1.简单介绍
Android系统中对原理的分析基本离不开对源码的阅读,我理解的原理分析:
原理分析 = 基本概念 + 源码分析 + 实践
正如创始人Linus Torvalds的名言:RTFSC(read the f**king source code)。本文也是按照上述结构来介绍AIDL的。
接下来先简单提一提IPC、序列化和Parcel三个概念。
2.IPC
1)进程与线程
A. 线程是CPU调度的最小单元,同时线程是一种有限的系统资源。
B. 进程一般指一个执行单元,在PC和移动设备上指一个程序或一个应用
C. 一个进程可以包含多个线程,包含与被包含的关系。
D. Java默认只有一个线程,叫主线程,在android中也叫做UI线程
2)IPC
A. 定义:IPC(inter-process Commnication)跨进程的通信,多进程之间的通信。
B. 为什么需要进程通信:我们知道Android一般情况下一个应用是默认运行在一个进程中,但有可能一个应用中需要采用多进程模式(process属性)来实现(比如获取多份内存空间),或者两个应用之间需要获取彼此之间的数据,还有AMS(系统服务)对每个应用中四大组件的管理,系统服务是运行在一个单独的进程中,这些统统需要IPC。
3)Android中的IPC
Android IPC方式:文件共享、ContentProvider(底层是Binder实现)、Socket、Binder(AIDL、Messenger)。
3.序列化
序列化是指将一个对象转化为二进制或者是某种格式的字节流,将其转换为易于保存或网络传输的格式的过程,反序列化则是将这些字节重建为一个对象的过程。Serializable和Parcelable接口可以完成对象的序列化。
1)Serializable
Serializable是Java提供的序列化接口,使用时只需要实现Serializable接口并声明一个serialVersionUID(用于反序列化)
2)Parcelable
A. writeToParcel:将对象序列化为一个Parcel对象,将类的数据写入外部提供的Parcel中
B. describeContents:内容接口描述,默认返回0
C. 实例化静态内部对象CREATOR实现接口Parcelable.Creator,需创建一个数组(newArray(int size)) 供外部类反序列化本类数组使用;createFromParcel创建对象
D. readFromParcel:从流里读取对象,写的顺序和读的顺序必须一致。
Serializable使用简单,但是开销很大(大量I/O操作),Parcelable是Android中的序列化方式,使用起来麻烦,但是效率很高,是Android推荐的方式。Parcelable主要用在内存序列化上,如果要将对象序列化到存储设备或者通过网络传输也是可以的,但是会比较复杂,这两种情况建议使用Serializable。
4.Parcel
Parcel主要就是用来进行IPC通信,是一种容器,他可以包含数据或者是对象引用,并且能够用于Binder的传输。同时支持序列化以及跨进程之后进行反序列化,同时其提供了很多方法帮助开发者完成这些功能。Parcel的读写都是一个指针操作的,有writeInt(int val)、writeString(String val)、setDataPosition(int val)、readInt()、readString()、recycle()方法。
二、AIDL
1.定义
AIDL:Android interface definition Language,Android 接口定义语言。使用aidl可以发布以及调用远程服务,实现跨进程通信。将服务的aidl放到对应的src目录,工程的gen目录会生成相应的接口类。
2.语法
AIDL的语法十分简单,与Java语言基本保持一致,主要规则有以下几点:
1)AIDL文件以 .aidl 为后缀名
2)AIDL支持的数据类型分为如下几种:
A. 八种基本数据类型:byte、char、short、int、long、float、double、boolean
String,CharSequence
B. 实现了Parcelable接口的数据类型
C. List 类型。List承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
D. Map类型。Map承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
3)AIDL文件可以分为两类
A. 一类用来声明实现了Parcelable接口的数据类型,以供其他AIDL文件使用那些非默认支持的数据类型。
B. 另一类是用来定义接口方法,声明要暴露哪些接口给客户端调用,定向Tag就是用来标注这些方法的参数值。
4)定向Tag
定向Tag表示在跨进程通信中数据的流向,用于标注方法的参数值。
A. in 表示数据只能由客户端流向服务端
B. out 表示数据只能由服务端流向客户端
C. inout 则表示数据可在服务端与客户端之间双向流通。
如果AIDL方法接口的参数值类型是:基本数据类型、String、CharSequence或者其他AIDL文件定义的方法接口,那么这些参数值的定向 Tag 默认是且只能是 in,所以除了这些类型外,其他参数值都需要明确标注使用哪种定向Tag。
5)明确导包
在AIDL文件中需要明确标明引用到的数据类型所在的包名,如java的import导入。
View Code

1 // UserControl.aidl
2 package com.haybl.aidl_test;
3
4 import com.haybl.aidl_test.User;
5
6 // Declare any non-default types here with import statements
7
8 interface UserController {
9
10 List<User> getUserList();
11
12 void addUserInOut(inout User user);
13 }

2)服务端
复制上述两个AIDL文件至服务端代码。创建 Service,在其中创建上面生成的 Binder 对象实例,实现接口定义的方法在 onBind() 中返回。

1 @Override
2 public IBinder onBind(Intent intent) {
3 Log.d(TAG, "onBind");
4 return stub;
5 }
6
7 private UserController.Stub stub = new UserController.Stub() {
8 @Override
9 public List<User> getUserList() throws RemoteException {
10 Log.d(TAG, "getUserList");
11 return mUserList;
12 }
13
14 @Override
15 public void addUserInOut(User user) throws RemoteException {
16 if (user != null) {
17 Log.d(TAG, "Server receive a new user by InOut = " + user.getName());
18 // 服务端改变数据,通过InOut Tag会同步到客户端。数据是双向流动的
19 user.setName("I'm changed");
20 mUserList.add(user);
21 } else {
22 Log.d(TAG, "Server receive a null by InOut");
23 }
24 }
25 };

本文详细介绍了Android中的AIDL,包括IPC基础知识、序列化机制、Parcel的使用以及AIDL的定义和语法。AIDL是实现跨进程通信的重要工具,它允许服务端和客户端通过接口进行数据交换。文中还探讨了Serializable和Parcelable的区别,并提供了服务端的实现概述。
883

被折叠的 条评论
为什么被折叠?



