概述
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对象
- 八种基本数据类型:byte、char、int、long、float、double、boolean
-
定向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:
参考:https://www.jianshu.com/p/29999c1a93cd