概述
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>() {@Overridepublic User createFromParcel(Parcel in) {return new User(in);}@Overridepublic User[] newArray(int size) {return new User[size];}};@Overridepublic int describeContents() {return 0;}@Overridepublic 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() {}@Overridepublic IBinder onBind(Intent intent) {return new MyBinder();}class MyBinder extends BookName.Stub {@Overridepublic String getName() throws RemoteException {return "西游记";}@Overridepublic 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文件定义
<serviceandroid: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;@Overrideprotected 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() {@Overridepublic 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的actionmIntent.setAction("com.aaa.aaa");//这里你需要设置你应用的包名mIntent.setPackage("com.baidu.bpit.aibaidu.aidl");bindService(mIntent, new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {bookName = BookName.Stub.asInterface(service);}@Overridepublic 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: 222111
复制代码
正确获取数据
定向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>() {@Overridepublic User createFromParcel(Parcel in) {return new User(in);}@Overridepublic User[] newArray(int size) {return new User[size];}};@Overridepublic int describeContents() {return 0;}@Overridepublic 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() {}@Overridepublic IBinder onBind(Intent intent) {return new MyBinder();}class MyBinder extends BookName.Stub {@Overridepublic String getName() throws RemoteException {return "西游记";}@Overridepublic 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;}@Overridepublic void addInout(User user) throws RemoteException {Log.d("mmmserver","服务端获取到:"+user.name);user.name="服务端更改";Log.d("mmmserver","服务端修改书名:"+user.name);}@Overridepublic void addIn(User user) throws RemoteException {}@Overridepublic void addout(User user) throws RemoteException {}}
}
复制代码
主要看inout方法,服务端接受到客户端传来的信息后,修改信息内容
客户端
private void initView() {findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {@Overridepublic 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
服务端
@Overridepublic 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() {@Overridepublic 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
服务端
@Overridepublic 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() {@Overridepublic 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: 参考:www.jianshu.com/p/29999c1a9…