项目中有个需求,在项目中实现基站定位,并将基站定位写成aidl,供给其他应用远程调用,实现方式就是 在项目中监听附近的基站,通过获取到基站的信息,去服务器上获取基站的经纬度,所以 问题来了,一般形式的aidl的做法是写一个aidl接口,需要的地方直接绑定服务,然后通过服务调用aidl接口,直接把结果返回给调用端,但是如果aidl的服务端里面是耗时操作的话,直接返回,那么会返回空值,因为数据加载的速度远远要小于代码执行速度,所以需要一个特殊的变种的aidl。
先看一下一般形式的aidl:`
1.第一步 写一个接口,参数类型为int,返回类型为int.
interface ILocationService {
int registerCallback(int sum);
}
2.第二步 在服务端新建一个Service
public class RemoteService extends Service {
public IBinder onBind(Intent intent) {
return mBinder;
}
private final ILocationService.Stub mBinder = new ILocationService.Stub() {
@Override
public int registerCallback(int data) throws RemoteException {
//直接返回计算结果给调用端 (问题就是 如果计算的过程是一个耗时操作,那么没等耗时操作做完就调用了return 那么客户端收到的是一个空值)
return data+666;
}
};
}
第三步:在客户端需要调用这个方法的时候加入
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = ILocationService.Stub.asInterface(service);
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
//获得计算结果
int sum= mService .registerCallback(22); //打印结果为688
第四步 也是最重要的一步:切记 在AndroidManifest.xml里面注册Service,另外如果是分成两个应用调用的时候
要把aidl文件一模一样的从服务端复制到客户端,如果参数是实体类的话,要把实体类的路径在服务端和客户端统一
<service
android:name=".RemoteService"
android:enabled="true"
android:process=":remote"
android:exported="true">
<intent-filter>
<action android:name="com.daking.aidl.RemoteService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
------------------------------------------------------华丽的分割线--------------------------------------------------------
以上方案只能解决正常的aidl通讯问题,参数是自定义实体类,和耗时访问操作还没有解决,所以以下是针对这个问题的解决方案:
第一步 依旧是新建aidl文件,区别是新增一个回调的aidl接口
package com.easyliu.demo.aidl;
//不要忽略这个import aidl文件不会自动生成,需要自己去引用这个包,地址要修改成你自己的地址
import com.easyliu.demo.aidl.IRemoteServiceCallback;
interface IRemoteService {
void registerCallback(IRemoteServiceCallback cb);
void unregisterCallback(IRemoteServiceCallback cb);
}
第二步 按你的需要建一个回调实体类的接口
package com.easyliu.demo.aidl;
//Station是我的实体类 里面存放着经纬度,基站等信息
import com.easyliu.demo.aidl.Station;
interface IRemoteServiceCallback {
//不需要返回类型,因为做耗时操作 return是没用的 传入的参数类型是Station
void valueChanged(in Station value); //in 输入,out 输出,
}
第三步 新建实体类 并建立实体类的aidl
首先先建立 实体类的aidl 否则可能编辑器会报文件必须唯一的问题
package com.easyliu.demo.aidl;
parcelable Station;
然后再建真正的实体类Station 切记 一定要实现Parcelable接口 给实体类序列化
这个实体类要存放在和aidl文件一直的java目录下,否则会报找不到这个实体类的错误
public class Station implements Parcelable{
private String cell_id;// cellid连接基站编码
private String lac;// lac连接基站位置区域码
private String mcc;// mcc MCC国家码
private String mnc;// mnc MNC网号
private String signalstrength;// signalstrength连接基站信号强度
private String NetType;
private String lat;
private String log;
public Station(){}
……
}
}
第四步 Service改造
public class RemoteService extends Service {
Station station2;
RemoteCallbackList<IRemoteServiceCallback> mCallbacks = new RemoteCallbackList<IRemoteServiceCallback>();
private int mValue = 0;
private static final int REPORT_MSG = 1;
private int position=0;
@Override
public void onCreate() {
//在oncreate里面可以放你的耗时操作
doInBackGround(stationBeans);
}
@Override
public void onDestroy() {
mCallbacks.kill();
mHandler.removeMessages(REPORT_MSG);
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
public void registerCallback(IRemoteServiceCallback cb) {
if (cb != null) mCallbacks.register(cb);
}
public void unregisterCallback(IRemoteServiceCallback cb) {
if (cb != null) mCallbacks.unregister(cb);
}
};
private final Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case REPORT_MSG: {
.//开始回调 一定要加上 否则回调失败
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
//将收到的值回调给aidl定义的方法
mCallbacks.getBroadcastItem(i).valueChanged(station2);
} catch (RemoteException e) {
}
}
//结束回调
mCallbacks.finishBroadcast();
} break;
default:
super.handleMessage(msg);
}
}
};
public void doInBackGround(final List<Station> data) {
//网络请求等耗时操作
OkGo.<String>post(Static.Url)
.tag(this)
.params("cid", data.get(0).getCell_id())
.params("lac", data.get(0).getLac())
.params("mnc", data.get(0).getMnc())
.execute(new StringCallback() {
@Override
public void onSuccess(Response<String> response) {
ResultData newInstructionEntity = new Gson().fromJson(response.body(), ResultData.class);
station2 = new Station();
station2.setLac(String.valueOf(newInstructionEntity.getData().getLatitude()));
station2.setLog(String.valueOf(newInstructionEntity.getData().getLongitude()));
//获得请求的返回值,并通过handler发消息通知
mHandler.sendEmptyMessage(REPORT_MSG);
}
})
}
}}
}
第五步:
客户端调用
// 绑定启动Service
Intent intent=new Intent(IRemoteService.class.getName());
intent.setPackage("com.easyliu.demo.aidldemo");
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = ILocationService.Stub.asInterface(service);
/加上绑定监听/
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
}
}
public void onServiceDisconnected(ComponentName className) {
mService = null;
}
};
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
@Override
public void valueChanged(Station value) throws RemoteException {
//这个回调就是获得结果的回调 value就是从服务器的请求结果 成功在客户端获取到服务端请求
……
}
};
笔记:
1.RemoteCallbackList。 使用这个类只要在Service使用单例模式就可以了,使用register和unregister方法来添加客户端的回调,使用时,先beginBroadcast,在getBroadcastItem,最后finishBroadcast。他的主要作用是可以把多个callback保存到列表里,在合适的时机同时回调,也可以防止重复的调用相同的任务
2. aidl通讯失败的大多数原因就是 aidl文件放的目录不对,或者aidl里面引用的实体类或者其他文件没有导包,和存放真正实体类的目录客户端和服务端没有统一。
3. 实体类一定要实现系列化才可以通过aidl通信