给大家讲解一下 AIDL原理分析

季春初始,天气返暖,新冠渐去,正值学习好时机。在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 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 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     };

复制代码

 

 

世卫官员:新冠疫苗问世前 生活方式有重大改变

你可能感兴趣的:(给大家讲解一下 AIDL原理分析)