## 概述
AIDL可以实现进程间的通信,由于每个进程都是运行在独立的空间,不同的进程想要交互需要借助一些特殊的方式,AIDL就是其中的一种,AIDL是一种模板,因为实际交互过程中,并不是AIDL起的作用,具体会在之后源码分析解释,AIDL的作用是为了避免重复编写代码而出现的一个模板
## 语法
AIDL的语法十分简单,与Java语言基本保持一致,需要记住的规则有以下几点:
* AIDL文件以 .aidl 为后缀名
AIDL支持的数据类型分为如下几种:
* 八种基本数据类型:byte、char、int、long、float、double、boolean
String,CharSequence,其中不支持short类型
* 实现了Parcelable接口的数据类型
* List 类型。List承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
* Map类型。Map承载的数据必须是AIDL支持的类型,或者是其它声明的AIDL对象
* 定向Tag。定向Tag表示在跨进程通信中数据的流向,用于标注方法的参数值,分为 in、out、inout 三种。其中
* in 表示数据只能由客户端流向服务端
* out 表示数据只能由服务端流向客户端
* inout 则表示数据可在服务端与客户端之间双向流通
* 此外,如果AIDL方法接口的参数值类型是:基本数据类型、String、CharSequence或者其他AIDL文件定义的方法接口,那么这些参数值的定向 Tag 默认是且只能是 in,所以除了这些类型外,其他参数值都需要明确标注使用哪种定向Tag。定向Tag具体的使用差别后边会有介绍
* 明确导包。在AIDL文件中需要明确标明引用到的数据类型所在的包名,即使两个文件处在同个包名下
## 服务端
* 先建立一个项目
* 由于要传输自定义User对象,所以定义一个User的aidl文件,直接生成

创建完成后,系统就会默认创建一个 aidl 文件夹,文件夹下的目录结构即是工程的包名,Book.aidi 文件就在其中
然后更改User.aidl文件内容
```
package com.baidu.bpit.aibaidu.aidl;
parcelable User;
```
* 然后生成一个User的类,实现Parcelable
```
public class User implements Parcelable {
public String name;
public User(String name){
this.name=name;
}
protected User(Parcel in) {
name = in.readString();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
}
}
```
* 然后在定义一个BookName的aidl文件,向客户端暴露可调用的接口,需要手动导入User,import com.baidu.bpit.aibaidu.aidl.User;
```
package com.baidu.bpit.aibaidu.aidl;
import com.baidu.bpit.aibaidu.aidl.User;
interface BookName {
String getName();
List<User> getList();
}
```
* 这时候重新build一下工程
* 现在需要来创建一个 Service 供客户端远程绑定了,返回你的自定义的Binder
```
public class ServiceService extends Service {
public ServiceService() {
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
class MyBinder extends BookName.Stub {
@Override
public String getName() throws RemoteException {
return "西游记";
}
@Override
public List<User> getList() throws RemoteException {
User user = new User("111");
User user1 = new User("222");
List<User> users = new ArrayList<>();
users.add(user1);
users.add(user);
return users;
}
}
}
```
* AndroidManifest.xml文件定义
```
<service
android:name=".ServiceService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.aaa.aaa" />
</intent-filter>
</service>
```
## 客户端
* 首先把服务端的aidl文件夹,整体复制到客户端
* 之后,需要创建和服务端User类所在的相同包名来存放 User类
* 
* 在MainActivity绑定服务端的service,点击按钮获取书名
```
public class MainActivity extends AppCompatActivity {
private BookName bookName;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindServer();
initView();
}
private void initView() {
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
Log.d("mmmgetName", bookName.getName());
List<User> list = bookName.getList();
for (User user : list) {
Log.d("mmmgetList", user.name);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
private void bindServer() {
Intent mIntent = new Intent();
//你定义的service的action
mIntent.setAction("com.aaa.aaa");
//这里你需要设置你应用的包名
mIntent.setPackage("com.baidu.bpit.aibaidu.aidl");
bindService(mIntent, new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookName = BookName.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}, BIND_AUTO_CREATE);
}
}
```
* 点击按钮打印
```
01-18 22:11:17.642 4542-4542/com.baidu.bpit.aibaidu.client D/mmmgetName: 西游记
01-18 22:11:17.643 4542-4542/com.baidu.bpit.aibaidu.client D/mmmgetList: 222
111
```
正确获取数据
## 定向TAG
有三种定向TAG
* inout:服务端修改数据,会同步到客户端,因此可以说数据是双向流动的
* in:数据只从客户端流向服务端,服务端修改数据不会影响客户端
* out:数据只能由服务端传向客户端,及时客户端传入一个对象,这个对象也是空的,即没有数据,服务端获取该对象后,对该对象任何操作都会同步到客户端这里
**修改aidl**
```
interface BookName {
String getName();
List<User> getList();
void addInout(inout User user);
void addIn(in User user);
void addout(out User user);
}
```
这次增加了三个方法addInout,addIn,addout,之后分别测试这三个方法
**修改User类**
User类需要添加俩个方法,一个无参构造,一个readFromParcel
```
public class User implements Parcelable {
public String name;
public User(){
}
public User(String name){
this.name=name;
}
protected User(Parcel in) {
name = in.readString();
}
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
}
public void readFromParcel(Parcel dest) {
name = dest.readString();
}
}
```
## 先测试一下inout
**服务端**
```
public class ServiceService extends Service {
public ServiceService() {
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
class MyBinder extends BookName.Stub {
@Override
public String getName() throws RemoteException {
return "西游记";
}
@Override
public List<User> getList() throws RemoteException {
User user = new User("111");
User user1 = new User("222");
List<User> users = new ArrayList<>();
users.add(user1);
users.add(user);
return users;
}
@Override
public void addInout(User user) throws RemoteException {
Log.d("mmmserver","服务端获取到:"+user.name);
user.name="服务端更改";
Log.d("mmmserver","服务端修改书名:"+user.name);
}
@Override
public void addIn(User user) throws RemoteException {
}
@Override
public void addout(User user) throws RemoteException {
}
}
}
```
主要看inout方法,服务端接受到客户端传来的信息后,修改信息内容
**客户端**
```
private void initView() {
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
User user = new User("客户端传入");
Log.d("mmmclient", "客户端向服务端传入一本书,书名:" + user.name);
bookName.addInout(user);
Log.d("mmmclient", "服务端修改书名后,书名:" + user.name);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
```
点击按钮后,向服务端传入数据,服务端收到数据,会对数据更改,客户端再次查看此数据,看是否同步
```
01-18 23:15:18.529 5606-5606/com.baidu.bpit.aibaidu.client D/mmmclient: 客户端向服务端传入一本书,书名:客户端传入
01-18 23:15:18.529 5527-5554/com.baidu.bpit.aibaidu.aidl D/mmmserver: 服务端获取到:客户端传入
01-18 23:15:18.530 5527-5554/com.baidu.bpit.aibaidu.aidl D/mmmserver: 服务端修改书名:服务端更改
01-18 23:15:18.530 5606-5606/com.baidu.bpit.aibaidu.client D/mmmclient: 服务端修改书名后,书名:服务端更改
```
看到服务端修改可以及时同步到客户端,这就是inout 数据双向流动
## 测试in
**服务端**
```
@Override
public void addIn(User user) throws RemoteException {
Log.d("mmmserverIn", "服务端获取到:" + user.name);
user.name = "服务端更改";
Log.d("mmmserverIn", "服务端修改书名:" + user.name);
}
```
**客户端**
```
private void initView() {
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
User user = new User("客户端传入");
Log.d("mmmclient", "客户端向服务端传入一本书,书名:" + user.name);
bookName.addIn(user);
Log.d("mmmclient", "服务端修改书名后,书名:" + user.name);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
```
当点击按钮,会发送数据到服务端,服务端会更改数据内容,客户端再次查看数据,看是否被改变
```
01-18 23:26:23.079 5815-5815/com.baidu.bpit.aibaidu.client D/mmmclient: 客户端向服务端传入一本书,书名:客户端传入
01-18 23:26:23.080 5736-5763/com.baidu.bpit.aibaidu.aidl D/mmmserverIn: 服务端获取到:客户端传入
01-18 23:26:23.081 5736-5763/com.baidu.bpit.aibaidu.aidl D/mmmserverIn: 服务端修改书名:服务端更改
01-18 23:26:23.081 5815-5815/com.baidu.bpit.aibaidu.client D/mmmclient: 服务端修改书名后,书名:客户端传入
```
看以看出服务端修改数据,并不会影响客户端
## 测试OUT
**服务端**
```
@Override
public void addout(User user) throws RemoteException {
Log.d("mmmserverout", "服务端获取到:" + user.name);
user.name = "服务端更改";
Log.d("mmmserverout", "服务端修改书名:" + user.name);
}
```
**客户端**
```
private void initView() {
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
User user = new User("客户端传入");
Log.d("mmmclient", "客户端向服务端传入一本书,书名:" + user.name);
bookName.addout(user);
Log.d("mmmclient", "服务端修改书名后,书名:" + user.name);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
```
客户端向服务端传入数据,服务端收到后,更改数据,客户端再次查看数据
```
01-18 23:36:21.997 6100-6100/com.baidu.bpit.aibaidu.client D/mmmclient: 客户端向服务端传入一本书,书名:客户端传入
01-18 23:36:21.998 6023-6037/com.baidu.bpit.aibaidu.aidl D/mmmserverout: 服务端获取到:null
服务端修改书名:服务端更改
01-18 23:36:21.998 6100-6100/com.baidu.bpit.aibaidu.client D/mmmclient: 服务端修改书名后,书名:服务端更改
```
可以看到服务端收到的是空对象,服务端更改影响客户端
GitHub: