CS(Client-Server)架构
1.客户端 Client
通过绑定服务端的Service来获取一个IBinder对象(也可以理解为是服务端AIDL的java类),然后用这个对象调用AIDL中定义的接口方法
步骤如下:
1)将服务端的jar包放在自己工程下的libs中,add as library导包
2)在Activity中绑定服务端的Service(通过Service的包名和类名)
3)创建一个ServiceConnection对象,实现onServiceConnected和onServiceDisconnected方法,在onServiceConnected方法中获取IBinder对象的代理,并转换为AIDL接口类型。
4)获取到IBinder对象的代理后,调用服务端提供的接口,做处理。
ComponentName componentName = new ComponentName("com.example.aidlserver", "com.example.aidlserver.AIDLService");
intent.setComponent(componentName);
bindService(intent, connection, BIND_AUTO_CREATE);
2.服务端 Server
是指提供数据或功能给客户端,它通过创建一个Service并在onBind()方法中返回一个IBinder对象来实现通信接口,该对象需要重写.aidl文件中定义的接口方法 。
步骤如下:
1)在工程下右键new 选择Module,创建一个library,在这里创建.aidl,这样做可以打成jar包给其他模块使用。
2)library下右键new AIDL创建一个 IMyAIDL.aidl文件 在这里定义给客户端提供的接口(代码AIDL里边没有提示只能自己手敲)
3)接口都定义好后,点下锤子编译,会自动生成AIDL对应的一个java类,不用管生成了就行,生成的位置如下:
4)在工程下创建一个AIDLService,实现AIDL接口,在onbind中返回binder对象(IMyAidlInterface.Stub()获得IBinder)。
5)在AndroidManifest.xml文件中将Service添加上,并设置如下属性:
设置intent-filter 定义一个action,让其他应用可以通过intent启动服务
<service
android:name=".AIDLService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.example.aidlserver.AIDL_SERVICE" />
</intent-filter>
</service>
6)这些都做好了之后,在Module下的build.gradle中找到编译jar包的地方运行编译个jar包出来,路径在build文件下的lib中,把这个jar包给客户端使。
客户端和服务端需要共享一个.aidl文件,用来声明通信接口和方法,该文件会被Android SDK工具转换成一个Java接口,该接口包含一个Stub类和一个Proxy类 。
如果单纯创建一个.aidl文件的话,那么客户端和服务端都需要放这个.aidl文件,并且包名必须完全一致,aidl文件一模一样。因为AIDL机制在底层是通过Binder实现的,Binder需要:
- 相同的接口描述:确保两端对方法签名、参数、返回值的理解一致
- 相同的包名:用于唯一标识和匹配接口
- 相同的接口名:用于查找和调用
(可以把服务端的aidl文件夹直接拷贝过来,aidl文件夹和java文件夹同级目录)
实践:
服务端:
1)创建aidl文件夹,创建.aidl文件,前提是build.gradle中得把aidl true,然后就能选择AIDL创建了,在aidl中定义好接口,然后点锤子Build,自动生成对应的接口java文件。
2)选择activity自动创建好一个activity不用改啥东西,然后创建service,new binder实例并实现(重写)接口,然后在onBind方法中返回binder,manifest里service保证是 android:exported="true"
客户端:
1)选择empty views activity创建活动,在oncreate里面bindService()
private void bindService() {
// 客户端绑定方式
Intent intent = new Intent();
// 直接指定组件名称,不依赖intent-filter
intent.setComponent(new ComponentName(
"com.ljx.aidltest", // 服务端包名
"com.ljx.aidltest.aidl.MyService2" // 服务类全名
));
bindService(intent, connection, Context.BIND_AUTO_CREATE);
//下面这个排查没连上的原因,首先排除服务端是否是OK状态,我没连上是因为包名错了,包名是build.gradle中那个包名
PackageManager pm = getPackageManager();
ComponentName cn = new ComponentName("com.ljx.aidltest", "com.ljx.aidltest.aidl.MyService2");
try {
getPackageManager().getPackageInfo("com.ljx.aidltest", 0);
Log.i("ljn", "服务端应用已安装");
} catch (PackageManager.NameNotFoundException e) {
Log.e("ljn", "服务端应用未安装");
}
}
private final ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.i("ljn", "onServiceConnected: ");
myAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);
try {
int count = myAidlInterface.getCount(1);
Log.i("ljn", "onServiceConnected: " + count);
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
manifest里面活动这样写的
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
AIDL进阶
一、实体类Parcelable
如果AIDL中定义的接口传的参数是基本数据类型,那么直接用即可。???如果不是基本数据类型,例如实体类这种包含多个类型的,需要创建实体Bean类,并序列化Parcelable 序列化就是将对象转换为可存储或可传输的状态。
1)服务端创建个实体类,并实现Parcelable接口将其序列化。序列化都是模板代码,如果不想自己写自动生成的话,AS需要安装插件Android Parcelable Code Generator,在需要序列化的类中,右键->generate->parcelable 选中需要序列化的成员变量,即可完成对象的序列化。
2)创建实体类同名的AIDL,写下面的代码就完事
package com.example.aidlserver;
// Declare any non-default types here with import statements
parcelable TestBean;
3)在给外部提供的AIDL的接口中传入实体类
二、AIDL参数的数据流向
aidl中这样使用:TestBean changeTestBean(in TestBean bean);
在传递序列化参数时,必须定义这些参数的数据流方向:in、out、inout
-
in:表示数据从客户端流向服务端,客户端会将参数对象复制一份并发送给服务端,服务端收到后可以对该对象进行修改,但不会影响客户端的原始对象 。
-
out:表示数据从服务端流向客户端,客户端会将参数对象的空引用发送给服务端,服务端收到后可以创建一个新的对象并赋值给该引用,然后返回给客户端,客户端会将原始对象替换成服务端返回的对象 。
-
inout:表示数据双向流动,客户端会将参数对象复制一份并发送给服务端,服务端收到后可以对该对象进行修改,并将修改后的对象返回给客户端,客户端会将原始对象替换成服务端返回的对象 。
三、oneway修饰
oneway 关键字用于修改远程调用的行为,用来修饰 AIDL 接口中的方法。
1.本地调用(同步调用)
如果 oneway 用于本地调用,则不会有任何影响,调用仍是同步调用。
2.远程调用(异步调用)
使用oneway时,远程调用不会阻塞;它只是发送事务数据并立即返回。将远程调用改为「异步调用」,使得远程调用变成非阻塞式的
Aidl中接口方法用了关键字oneway,主要是将数据或通知发送给服务端,不需要等待服务端的回调,是非阻塞的。
注意:oneway不能用于修饰有返回值的方法