1.创建.aidl文件
2.SDK生成对应.java文件和Stub内部类
3.通过Service子类将接口暴露给外界
用Java编程语言来构造.aidl文件。每个.aidl文件必须定义一个带方法声明的接口。
AIDL支持以下数据类型:
1.Java基本类型,即int、long、char等;
2.String;
3.CharSequence;
4.List
List中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。
5.Map
Map中的所有元素都必须是AIDL支持的数据类型、其他AIDL接口或你之前声明的Parcelable实现类。
6.其他类型,必须要有import语句,即使它跟.aidl是同一个包下。
AIDL中的方法和变量
• 方法可有零、一或多个参数,可有返回值或void。
• 所有非基本类型的参数都需要标签来表明这个数据的去向:
1.in,表示此变量由客户端设置;
2.out,表示此变量由服务端设置;
3.inout,表示此变量可由客户端和服务端设置;
4.基本类型只能是in。
• 只expose方法,不会expose静态变量。
服务端——服务(Manager.aidl)
// Manager.aidl
package com.dream.aidl;
// Declare any non-default types here with import statements
import com.dream.aidl.IdCardInfoCallBack;
interface Manager {
void readCard(String serialPort);//开启读卡
void clearCard();//清除读卡内容
void stopReadCard();//停止读卡
}
•当编译APP时,SDK工具会将项目/src/main/aidl目录下的.aidl文件一个个在项目/build/generated/source/aidl目录下生成IBinder接口.java文件。两个文件名一样,只是后缀不同。如Manager.aidl生成Manager.java。
•Stub内部类
▫ .aidl文件编译后生成的.java文件中自动生成的内部类。
▫ public static abstract声明。
▫ extends android.os.Binder。
▫ 实现.aidl文件中定义的接口,且声明其所有方法。
•实现Stub内部类要注意
▫ 对于传过来的调用,无法保证是在主线程中执行的。Service必须要考虑多线程和线程安全。
▫ 默认情况下,RPC都是异步的。**避免在主线程中调用AIDL**,不然可能会导致ANR。
▫ 不能给调用方回抛异常。
需要在服务端创建一个Service子类,并在onBind()中返回Stub内部类。
/**
* 服务端调用读卡的服务
*/
public class ReadCardService extends Service {
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
/**
* 实现aidl中的方法
* 不同方法调用不同读卡方法
*/
private Manager.Stub mStub = new Manager.Stub() {
@Override
public void readCard(String serialPort) throws RemoteException {
//开启读卡
}
@Override
public void clearCard() throws RemoteException {
//清除读卡
}
@Override
public void stopReadCard() throws RemoteException {
//停止读卡
}
};
}
AndroidManifest.xml 配置,设置exported为true、自定义action名等。
<service
android:name=".ReadCardService"
android:enabled="true"
android:exported="true"
android:process=":remote">
<intent-filter>
<action android:name="com.dream.idcard">action>
intent-filter>
service>
• 若客户端组件和服务分开在不同APP,那么客户端所在APP的/src/main/aidl目录下必须要有一份.aidl副本。
• 开启AIDL服务。
Intent intent = new Intent();
//android 5.0以后直设置action不能启动相应的服务,需要设置packageName或者Component。
intent.setAction("com.dream.idcard");
intent.setComponent(new ComponentName("com.dream.readidcard", "com.dream.readidcard.ReadCardService"));
//绑定服务
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
• 客户端建立连接后,要将接受到的AIDL服务端iBinder转换为客户端所在APP的/src/main/aidl中的Manager.aidl类型。
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
try {
mStub = Manager.Stub.asInterface(iBinder);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
mStub = null;
}
};
• 可像普通对象一样调用mStub 。注意,AIDL服务默认是运行在主线程中,若里面有耗时操作,应该在子线程中调用AIDL。
1.创建回调使用的aidl:IdCardInfoCallBack.aidl
// IdCardInfoCallBack.aidl
package com.dream.aidl;
/**
* Created by mx on 2017-11-8.
* 读卡信息回调
*/
import com.dream.aidl.IdCardInfo;
interface IdCardInfoCallBack {
void CallBackInfo(in IdCardInfo idCardInfo);
}
2.Manager.aidl提供给客户端注册、注销的方法。
// Manager.aidl
package com.dream.aidl;
// Declare any non-default types here with import statements
import com.dream.aidl.IdCardInfoCallBack;
interface Manager {
void readCard(String serialPort);//开启读卡
void clearCard();//清除读卡内容
void stopReadCard();//停止读卡
void registerCallBack(in IdCardInfoCallBack cb);//注册回调接口
void unregisterCallBack(in IdCardInfoCallBack cb);//注销回调接口
}
3.实现类:ReadCardService
/**
* 服务端调用读卡的服务
*/
public class ReadCardService extends Service {
/**
* 读卡回调
*/
OnReadCardListener onReadCardListener = new OnReadCardListener() {
@Override
public void onReadCardSuccess(IdCardInfo idCardInfo) {
sendResponse(idCardInfo);
}
};
//我们在服务端通知客户端消息的时候 也害怕 服务端 会异常销毁 导致客户端收不到消息
//好在谷歌早就为我们考虑到这种情况 提供了RemoteCallbackList 来完成对应的功能
//避免我们再重复一遍上述的过程
private RemoteCallbackList mCallBacks = new RemoteCallbackList<>();
/**
* 实现aidl中的方法
* 不同方法调用不同读卡方法
*/
private Manager.Stub mStub = new Manager.Stub() {
@Override
public void readCard(String serialPort) throws RemoteException {
//开启读卡
}
@Override
public void clearCard() throws RemoteException {
//清除读卡
}
@Override
public void stopReadCard() throws RemoteException {
//停止读卡
}
@Override
public void registerCallBack(IdCardInfoCallBack cb) throws RemoteException {
if (null != cb) {
//注册回调
mCallBacks.register(cb);
}
}
@Override
public void unregisterCallBack(IdCardInfoCallBack cb) throws RemoteException {
if (null != cb) {
//注销回调
mCallBacks.unregister(cb);
}
}
};
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
/**
* 发送读取的身份证信息
*
* @param idCardInfo
*/
private void sendResponse(IdCardInfo idCardInfo) {
// 以广播的方式进行客户端回调
int len = mCallBacks.beginBroadcast();
for (int i = 0; i < len; i++) {
try {
mCallBacks.getBroadcastItem(i).CallBackInfo(idCardInfo);
} catch (RemoteException e) {
e.printStackTrace();
}
}
// 记得要关闭广播
mCallBacks.finishBroadcast();
}
@Override
public void onDestroy() {
super.onDestroy();
mCallBacks.kill();//销毁回调资源 否则要内存泄露
}
}
4.客户端调用
//在建立连接时候调用 注册AIDL回调
mStub.registerCallBack(idCardInfoCallBack);
/**
* AIDL服务端返回数据
*/
protected IdCardInfoCallBack idCardInfoCallBack = new IdCardInfoCallBack.Stub() {
@Override
public void CallBackInfo(IdCardInfo idCardInfo) throws RemoteException {
if (null != idCardInfo) {
//对UI进行操作
}
}
};