文章大概的说了一点Car模块的binder交互,说的比较乱,只适合我个人研读。🐶🐶🐶
套路1:
服务的aidl 设置get() set() callback() register(IIoStatsListener listener)
管理类怎么写?
首先管理类需要让第三方使用到所有服务端的接口以及回调功能,所以我们要提供注册的功能以及解注册的功能。
既然要提供接口回调,那么就需要在管理类中写接口,将服务端的回调函数通过接口传给使用的第三方。
- 首先需要将服务端的binder实例化,使用内部类引用外部类使用软引用。然后通过handler将服务端传来的数据发出去。通过加入的服务端集合遍历,将所有的数据回调出去。
- 在register函数里面,因为要注册回调。所以我们需要实例化ListenerToService ,因为只有在null的情况下才去去new一个对象,
所以需要判断是否为空。 - 因为有多个listener所以一般都是将listener加入到一个集合中。
- unregisterListener 里面先判断在回调集合中是否已经删除了这个监听,如果没有删除就直接移除。
private static final class ListenerToService extends IIoStatsListener.Stub {
private final WeakReference<CarStorageMonitoringManager> mManager;
ListenerToService(CarStorageMonitoringManager manager) {
mManager = new WeakReference<>(manager);
}
@Override
public void onSnapshot(IoStats snapshot) {
CarStorageMonitoringManager manager = mManager.get();
if (manager != null) {
manager.mMessageHandler.sendEvents(Collections.singletonList(snapshot));
}
}
}
public CarStorageMonitoringManager(IBinder service, Handler handler) {
mService = ICarStorageMonitoring.Stub.asInterface(service);
mMessageHandler = new SingleMessageHandler<IoStats>(handler, MSG_IO_STATS_EVENT) {
@Override
protected void handleEvent(IoStats event) {
for (IoStatsListener listener : mListeners) {
listener.onSnapshot(event);
}
}
};
}
public void registerListener(IoStatsListener listener) throws CarNotConnectedException {
try {
if (mListeners.isEmpty()) {
if (mListenerToService == null) {
mListenerToService = new ListenerToService(this);
}
mService.registerListener(mListenerToService);
}
mListeners.add(listener);
} catch (IllegalStateException e) {
checkCarNotConnectedExceptionFromCarService(e);
} catch (RemoteException e) {
throw new CarNotConnectedException();
}
}
public void unregisterListener(IoStatsListener listener) throws CarNotConnectedException {
try {
if (!mListeners.remove(listener)) {
return;
}
if (mListeners.isEmpty()) {
mService.unregisterListener(mListenerToService);
mListenerToService = null;
}
} catch (IllegalStateException e) {
checkCarNotConnectedExceptionFromCarService(e);
} catch (RemoteException e) {
throw new CarNotConnectedException();
}
}
服务端怎么写?
- 要继承服务的aidl
- 复写aidl中的方法,在类的构造方法中可以实例化一些需要的类和方法。
- 服务端的回调,可以通过RemoteCallbackList来处理,实例化后,在register方法中注册RemoteCallbackList。这样做的好处是极大的简化了回调带来的多余代码。
1.CarStorageMonitoringService extends ICarStorageMonitoring.Stub
2.private final RemoteCallbackList<IIoStatsListener> mListeners = new RemoteCallbackList<>();
3.注册回调
@Override
public void registerListener(IIoStatsListener listener) {
mListeners.register(listener);
}
@Override
public void unregisterListener(IIoStatsListener listener) {
mListeners.unregister(listener);
}
4.通过RemoteCallbackList将数据发送给注册端
private void dispatchNewIoEvent(IoStats delta) {
final int listenersCount = mListeners.beginBroadcast();
IntStream.range(0, listenersCount).forEach(
i -> {
try {
mListeners.getBroadcastItem(i).onSnapshot(delta);
} catch (RemoteException e) {
Log.w(TAG, "failed to dispatch snapshot", e);
}
});
mListeners.finishBroadcast();
}
套路二
服务端怎么写?
先看看callback 的aidl 怎么写。
interface IVehicleCallback {
oneway onPropertyEvent(vec<VehiclePropValue> propValues);
oneway onPropertySet(VehiclePropValue propValue);
oneway onPropertySetError(StatusCode errorCode,
int32_t propId,
int32_t areaId);
};
java端这样写:
1. VehicleHal extends IVehicleCallback.Stub
2. 因为callback都是通过服务端进行注册的,所以这里面使用构造方法传入service
public VehicleHal(IVehicle vehicle) {
....
mHalClient = new HalClient(vehicle, mHandlerThread.getLooper(), this /*IVehicleCallback*/);
....
}
3.注册,反注册
mHalClient.subscribe(opts);
mHalClient.unsubscribe(property);
4.将callback 回调出去。因为有很多模块使用这个回调。所以在添加回调的时候,先将使用这个集合的回调加进去。然后遍历就知道有多少个服务要使用这个callback了。
private final SparseArray<HalServiceBase> mServicesToDispatch = new SparseArray<>();
5.通过遍历,将数据分发给各个service 然后清空集合。下次在来数据的时候,重新执行。
for (HalServiceBase s : mServicesToDispatch) {
s.handleHalEvents(s.getDispatchList());
s.getDispatchList().clear();
}
6.看一下服务类注册callback的类mhalClient,构造方法传入了callback,提供了处理aidl里面的接口能力,但是具体的工作还是VehicleHal来实现的。
HalClient(IVehicle vehicle, Looper looper, IVehicleCallback callback) {
mVehicle = vehicle;
Handler handler = new CallbackHandler(looper, callback);
mInternalCallback = new VehicleCallback(handler);
}
7.public void subscribe(SubscribeOptions... options) throws RemoteException {
mVehicle.subscribe(mInternalCallback, new ArrayList<>(Arrays.asList(options)));
}
public void unsubscribe(int prop) throws RemoteException {
mVehicle.unsubscribe(mInternalCallback, prop);
}
8.看看具体需要使用到callback 与一些接口的服务类是怎么处理的。
PropertyHalService extends HalServiceBase
public PropertyHalService(VehicleHal vehicleHal) {}
9. public CarPropertyValue getProperty(int mgrPropId, int areaId) {
value = mVehicleHal.get(halPropId, areaId);
}
10. 通过HalServiceBase这个抽象类,handleHalEvents会接收所有的来自于VehicleHal的回调。
因为不同的service模块可能接收的id都不一样,所以再回调的是时候,先判断用户需要的ID,过滤出自己需要的id,然后回调到接口里面。
if (listener != null) {
for (VehiclePropValue v : values) {
.....
listener.onPropertyChange(mEventsToDispatch);
mEventsToDispatch.clear();
.....
}
}
前面的10步我们看到了hal service 把底层的数据交给了base类。那么最后需要在各个具体的服务类里面去完成释放给客户端的任务.
CarPropertyService extends ICarProperty.Stub
implements CarServiceBase, PropertyHalService.PropertyHalListener {
1.首先在构造方法传入了PropertyHalService
public CarPropertyService(Context context, PropertyHalService hal) {
mHal = hal;
mContext = context;
}
2.然后是注册回调,根据id ,率进行注册。
这里面没有用remotecallbacklist 而是直接new了一个client 来管理客户端的绑定解绑以及客户端死亡回调再次释放资源。
registerListener(int propId, float rate, ICarPropertyEventListener listener)
3.套路来了,所有的客户端注册都可以这样写!
实现客户端的死亡回调
private class Client implements IBinder.DeathRecipient {
//客户传入的listener
private final ICarPropertyEventListener mListener;
//binder
private final IBinder mListenerBinder;
private final SparseArray<Float> mRateMap = new SparseArray<Float>(); // key is propId
Client(ICarPropertyEventListener listener) {
mListener = listener;
mListenerBinder = listener.asBinder();
try {
//注册死亡回调
mListenerBinder.linkToDeath(this, 0);
} catch (RemoteException e) {
Log.e(TAG, "Failed to link death for recipient. " + e);
throw new IllegalStateException(Car.CAR_NOT_CONNECTED_EXCEPTION_MSG);
}
//使用map 映射 每个客户端binder 对应一个client对象
mClientMap.put(mListenerBinder, this);
}
//速率表
void addProperty(int propId, float rate) {
mRateMap.put(propId, rate);
}
/**
* Client died. Remove the listener from HAL service and unregister if this is the last
* client.
*/
@Override
public void binderDied() {
if (DBG) {
Log.d(TAG, "binderDied " + mListenerBinder);
}
//如果客户端挂了,那么就执行解绑操作,释放资源
for (int i = 0; i < mRateMap.size(); i++) {
int propId = mRateMap.keyAt(i);
CarPropertyService.this.unregisterListenerBinderLocked(propId, mListenerBinder);
}
//同时删除map集合里面的值
this.release();
}
ICarPropertyEventListener getListener() {
return mListener;
}
IBinder getListenerBinder() {
return mListenerBinder;
}
float getRate(int propId) {
// Return 0 if no key found, since that is the slowest rate.
return mRateMap.get(propId, (float) 0);
}
void release() {
mListenerBinder.unlinkToDeath(this, 0);
mClientMap.remove(mListenerBinder);
}
void removeProperty(int propId) {
mRateMap.remove(propId);
if (mRateMap.size() == 0) {
// Last property was released, remove the client.
this.release();
}
}
}
4.将套路用到register方法里面,这个注册主要是马上将一部分值回调给注册的客户端。
private final Map<IBinder, Client> mClientMap = new ConcurrentHashMap<>();
registerListener(int propId, float rate, ICarPropertyEventListener listener){
IBinder listenerBinder = listener.asBinder();
synchronized (mLock) {
// 通过binder获取每个客户端的实例
Client client = mClientMap.get(listenerBinder);
if (client == null) {
//新建一个
client = new Client(listener);
}
//添加id 对应的速率 映射
client.addProperty(propId, rate);
// Insert the client into the propId --> clients map
//每个property id 对应的多个client端
List<Client> clients = mPropIdClientMap.get(propId);
if (clients == null) {
clients = new CopyOnWriteArrayList<Client>();
mPropIdClientMap.put(propId, clients);
}
if (!clients.contains(client)) {
clients.add(client);
}
// 注册hal 服务的listener
if (!mListenerIsSet) {
mHal.setListener(this);
}
// 如果速率有更新那么从hal 服务那里更新订阅服务
if (rate > mHal.getSampleRate(propId)) {
mHal.subscribeProperty(propId, rate);
}
}
//注册回调的时候,会给每个id值 get一个然后回调出去值
List<CarPropertyEvent> events = new LinkedList<CarPropertyEvent>();
for (int areaId : mConfigs.get(propId).getAreaIds()) {
CarPropertyValue value = mHal.getProperty(propId, areaId);
CarPropertyEvent event = new CarPropertyEvent(
CarPropertyEvent.PROPERTY_EVENT_PROPERTY_CHANGE, value);
events.add(event);
}
try {
listener.onEvent(events);
} catch (RemoteException ex) {
Log.e(TAG, "onEvent calling failed: " + ex);
}
}
5.看看来自hal service 的接口处理的id值更新
@Override
public void onPropertyChange(List<CarPropertyEvent> events) {
//局部变量
Map<IBinder, Pair<ICarPropertyEventListener, List<CarPropertyEvent>>> eventsToDispatch =
new HashMap<>();
//遍历集合获取id
for (CarPropertyEvent event : events) {
int propId = event.getCarPropertyValue().getPropertyId();
//通过id这个key 找到client集合
List<Client> clients = mPropIdClientMap.get(propId);
//如果是空的那么就是没有注册者注册过这个id,跳过继续循环
if (clients == null) {
Log.e(TAG, "onPropertyChange: no listener registered for propId=0x"
+ toHexString(propId));
continue;
}
//遍历客户端的集合
for (Client c : clients) {
//每个客户端对应一个binder
IBinder listenerBinder = c.getListenerBinder();
//通过pair对 寻找 这个客户端binder对应的所有的property id 的集合
Pair<ICarPropertyEventListener, List<CarPropertyEvent>> p =
eventsToDispatch.get(listenerBinder);
//如果没有
if (p == null) {
// Initialize the linked list for the listener
//frist 是binder second new了一个链表CarPropertyEvent
p = new Pair<>(c.getListener(), new LinkedList<CarPropertyEvent>());
//添加映射关系
eventsToDispatch.put(listenerBinder, p);
}
//顺便给链表里面添加值
p.second.add(event);
}
}
// Parse the dispatch list to send events
//解析值,然后发起回调
for (Pair<ICarPropertyEventListener, List<CarPropertyEvent>> p: eventsToDispatch.values()) {
try {
//first里面是listener,所以调用listener的方法将second里面的数据发出去。形成对应的关系。牛逼的设计!!!
p.first.onEvent(p.second);
} catch (RemoteException ex) {
// If we cannot send a record, its likely the connection snapped. Let binder
// death handle the situation.
Log.e(TAG, "onEvent calling failed: " + ex);
}
}
}
客户端管理类怎么写?
上面用了大量的篇幅说明了,hal到服务端的套路。那么客户端也不可能直接用服务端的数据,需要管理起来,让客户端不必要都去一个一个实现其服务端的aidl ,那就很麻烦了,而且对多客户端很不友好,所以就有了manager。
1.先看看客户端的构造方法
//首先要传入binder 这个是客户端必须要给的。
public CarPropertyManager(IBinder service, Handler handler, boolean dbg, String tag) {}
2.
public boolean registerListener(CarPropertyEventListener listener, int propertyId, float rate)
throws CarNotConnectedException {
synchronized (mActivePropertyListener) {
if (mCarPropertyEventToService == null) {
//把这个manager传入到CarPropertyEventListenerToService,顺便实例化,可以接收监听的操作了
mCarPropertyEventToService = new CarPropertyEventListenerToService(this);
}
//是否需要更新服务
boolean needsServerUpdate = false;
CarPropertyListeners listeners;
listeners = mActivePropertyListener.get(propertyId);
if (listeners == null) {
//这个类对数据进行了处理,比如时间的新旧,速率变化
listeners = new CarPropertyListeners(rate);
mActivePropertyListener.put(propertyId, listeners);
needsServerUpdate = true;
}
if (listeners.addAndUpdateRate(listener, rate)) {
needsServerUpdate = true;
}
if (needsServerUpdate) {
//直接通过服务重新注册
if (!registerOrUpdatePropertyListener(propertyId, rate)) {
return false;
}
}
}
return true;
}
}
private class CarPropertyEventListenerToService extends ICarPropertyEventListener.Stub{
private final WeakReference<CarPropertyManager> mMgr;
CarPropertyEventListenerToService(CarPropertyManager mgr) {
mMgr = new WeakReference<>(mgr);
}
@Override
public void onEvent(List<CarPropertyEvent> events) throws RemoteException {
CarPropertyManager manager = mMgr.get();
if (manager != null) {
manager.handleEvent(events);
}
}
}
套路三
准备在下一个篇幅去记录,这种方法其实比较好用,使用的场景是通过服务来获取资源数据。