在之前的文章中简单的介绍了跨进程通信的基础知识、跨进程通讯的集中方式以及简单的记录了Binder的基础知识,接下来讨论一下Android中常用的AIDL以及Messenger这两种多进程通信方式。
AIDL
AIDL全称Android Interface Definition Language,即Android接口定义语言,是Binder机制实现的Android IPC使用比较广泛的工具,使用AIDL进行进程间通讯需要定义client和Server,其中client和Server可以在同一个应用也可以在不同的应用。接下来将通过相关代码展示AIDL的基本实现逻辑。
首先新建两个app项目,分为定义为client端和Server端,
Server端
建立相关的aidl文件以及bean对象
在Server端main目录下建立aidl文件夹以及相关的.aidl文件,并copy到client端,如图所示:
这里主要定义了IVuiService.aidl以及UserInfo.aidl,这里需要注意命名规范,一般都是以“I”开头,其内容如下:
// IVuiService.aidl
package com.example.aidltestserver;
import com.example.aidltestserver.UserInfo;
import com.example.aidltestserver.IUserInfoUpdateCallback;
// Declare any non-default types here with import statements
interface IVuiService {
String getUserName();
boolean setUserInfo(in UserInfo info);
UserInfo getUserInfo();
boolean addUserInfoUpdateCallback(in IUserInfoUpdateCallback callback);
}
// IUserInfoUpdateCallback.aidl
package com.example.aidltestserver;
// Declare any non-default types here with import statements
parcelable UserInfo;
aidl文件编写完成之后,要进行makeproject,As会自动生成相关的.java文件,这样在service中才可以使用。
这里有以下几点需要注意的地方:
- AIDL支持的数据类型
- 基本数据类型
- String、CharSequence
- List,HashMap,其内部元素也需要被AIDL支持
- 实现Parcelable接口的对象
- AIDL 类型的接口,非普通接口
- 方向标识
- in 表示输入参数,即服务端可以修改该类型
- out 表示输出参数,即客户端可以修改该类型,客户端不行
- inout 表示客户端和服务端都可以修改该类型
- 传递序列化对象,如下:
首先需要声明相关的bean对象,实现Parcelable接口
public class UserInfo implements Parcelable {
private String name = "张三";
@RequiresApi(api = Build.VERSION_CODES.Q)
public UserInfo(Parcel in) {
name = in.readString();
}
public UserInfo() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@RequiresApi(api = Build.VERSION_CODES.Q)
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
}
@RequiresApi(api = Build.VERSION_CODES.Q)
public void readFromParcel(Parcel source) {
name = source.readString();
}
public static final Creator<UserInfo> CREATOR = new Creator<UserInfo>() {
@RequiresApi(api = Build.VERSION_CODES.Q)
@Override
public UserInfo createFromParcel(Parcel in) {
return new UserInfo(in);
}
@Override
public UserInfo[] newArray(int size) {
return new UserInfo[size];
}
};
}
之后声明新建UserInfo.aidl文件,内容如下:
// IUserInfoUpdateCallback.aidl
package com.example.aidltestserver;
// Declare any non-default types here with import statements
parcelable UserInfo;
注意这里的parcelable是小写p开头,同时bean对象需要拷贝到client端。
5. 在AIDL文件中,除了基本数据类型、String、charsequence、list、map,其他在文件中使用的类,必须使用import语句导入相关的package,否则会报错。
新建service类,实现接口,处理客户端请求并将Binder返回
public class ServerService extends Service {
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new ServerBinder();
}
class ServerBinder extends IVuiService.Stub{
@Override
public String getUserName() throws RemoteException {
return "测试";
}
@Override
public boolean setUserInfo(com.example.aidltestserver.UserInfo info) throws RemoteException {
return false;
}
@Override
public com.example.aidltestserver.UserInfo getUserInfo() throws RemoteException {
UserInfo userInfo = new UserInfo();
userInfo.setName("李四");
return userInfo;
}
@Override
public boolean addUserInfoUpdateCallback(IUserInfoUpdateCallback callback) throws RemoteException {
return false;
}
}
}
将service暴露出去
<service android:name=".service.ServerService">
<intent-filter>
<action android:name="com.example.aidltestserver.service.ServerService"/>
</intent-filter>
</service>
以上是Server端的基本的代码了
client端逻辑
copy aidl文件以及bean文件
启动service
private void startConnectService() {
Intent intent = new Intent(ACTION_SERVICE);
intent.setPackage(ACTION_SERVICE_PACKAGE);
bindService(intent,connection, Context.BIND_AUTO_CREATE);
}
服务连接之后通信
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "onServiceConnected: "+name);
if (service != null){
iVuiService = IVuiService.Stub.asInterface(service);
try {
String userName = iVuiService.getUserName();
Log.e(TAG, "onServiceConnected: username"+userName );
UserInfo userInfo = iVuiService.getUserInfo();
Log.e(TAG, "onServiceConnected: username.getName"+userInfo.getName() );
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected: " );
}
};
AIDL重连
AIDL断开重连,实际上就是对service的重新绑定,所以监听到服务断开的时候进行重新绑定。
- 在bindService的时候,会有个服务连接状态监听,可以通过这个监听去监听断开的状态,然后进行重新绑定:
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected: " );
//重新绑定
}
- Binder死亡代理
死亡代理要在service连接成功和断开的时候分别注册/解注册
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "onServiceConnected: "+name);
if (service != null){
try {
//设置死亡代理
service.linkToDeath(deathRecipient,0);
} catch (RemoteException e) {
e.printStackTrace();
}
iVuiService = IVuiService.Stub.asInterface(service);
try {
String userName = iVuiService.getUserName();
Log.e(TAG, "onServiceConnected: username"+userName );
UserInfo userInfo = iVuiService.getUserInfo();
Log.e(TAG, "onServiceConnected: username.getName"+userInfo.getName() );
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected: " );
//重新绑定
}
};
private IBinder.DeathRecipient deathRecipient=new IBinder.DeathRecipient() {
@Override
public void binderDied() {
//解绑
if(iVuiService != null){
iVuiService.asBinder().unlinkToDeath(deathRecipient,0);
iVuiService = null;
}
//断开重新绑定
}
};