HvacController是HvacApp与CarService之间的信息传输控制器,本质上也是一个Service。
public class HvacController extends Service { private final Binder mBinder = new LocalBinder(); @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; } @Override public IBinder onBind(Intent intent) { return mBinder; } public class LocalBinder extends Binder { HvacController getService() { return HvacController.this; } } ...}在Hvac中的设置及获取数据的利用都是通过HvacController举行的,在HvacController启动时会获取一个Car实例,并通过connect方法毗连CarService。当毗连CarService成功后初始化CarHvacManager并通过CarHvacManager获取车辆支持的属性列表,以及获取界面所需的底子数据。
@Overridepublic void onCreate() { super.onCreate(); if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { // 毗连 CarService mCarApiClient = Car.createCar(this, mCarServiceConnection); mCarApiClient.connect(); }}private final ServiceConnection mCarServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mHvacManagerReady) { try { // 毗连上CarService后,获取到此中的HvacManager. initHvacManager((CarHvacManager) mCarApiClient.getCarManager(Car.HVAC_SERVICE)); // 毗连成功后,叫醒正在等待CarHvacManager的线程 mHvacManagerReady.notifyAll(); } catch (CarNotConnectedException e) { Log.e(TAG, "Car not connected in onServiceConnected"); } } } @Override public void onServiceDisconnected(ComponentName name) { }};向CarService获取数据必要先得到CarHvacManager的实例,以是在毗连成功后,调用mHvacManagerReady.notifyAll() 叫醒所有之前等待CarHvacManager实例的线程
// HvacUiService.java - mServiceConnection{ final Runnable r = () -> { // hvac控制器从车辆革新其值后,绑定所有值。 mHvacPanelController.updateHvacController(mHvacController); }; if (mHvacController != null) { mHvacController.requestRefresh(r, new Handler(context.getMainLooper())); }}// HvacController.javapublic void requestRefresh(final Runnable r, final Handler h) { final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... unused) { synchronized (mHvacManagerReady) { while (mHvacManager == null) { try { mHvacManagerReady.wait(); } catch (InterruptedException e) { // We got interrupted so we might be shutting down. return null; } } } // 革新数据 fetchTemperature(DRIVER_ZONE_ID); fetchTemperature(PASSENGER_ZONE_ID); fetchFanSpeed(); ... return null; } @Override protected void onPostExecute(Void unused) { // 切换到主线程中实验runnable h.post(r); } }; task.execute();}private void fetchFanSpeed() { if (mHvacManager != null) { int zone = SEAT_ALL; //特定于汽车的办理方法。 try { int speed = mHvacManager.getIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone); mDataStore.setFanSpeed(speed); } catch (android.car.CarNotConnectedException e) { Log.e(TAG, "Car not connected in fetchFanSpeed"); } }}上面的代码就是利用AsyncTask在子线程中等待CarHvacManager的实例,然后革新数据并存储在DatStore中。
必要注意一点的是while (mHvacManager == null)不能更换成if(mHvacManager == null),这是由于Java有个叫“spurious wakeup”的征象,即线程在不应醒过来的时间醒过来。
A thread can wake up without being notified, interrupted, or timing out, a so-called spurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied.
一个线程有大概会在未被关照、打断、或超时的环境下醒来,这就是所谓的“spurious wakeup”。只管实际上这种环境很少发生,应用步调仍旧必须对此有所防范,本领是查抄正常的导致线程被叫醒的条件是否满足,假如不满足就继承等待。
3.6 Car API
Car是Android汽车平台最高品级的API,为外界提供汽车所有服务和数据访问的接口,提供了一系列与汽车有关的API。它不光仅可以提供HvacManger,像车辆的速率、档位状态等等所有与汽车有关的信息都可以从Car API中获取。
Hvac中的CarHvacManager实现了CarManagerBase接口,而且只要是作为CarXXXManager, 都必要实现CarManagerBase接口,如CarCabinManager,CarSensorManager等都实现了该接口。
CarHvacManager的控制利用是通过CarPropertyManager来完成的,CarPropertyManager同一控制汽车属性干系的利用。CarHvacManager只是控制与Hvac干系的利用,在汽车中尚有许多属性控制的Manager,如传感器,座舱等属性的控制,他们都是通过CarPropertyManager举行属性利用,通过在利用时传入的属性ID,属性地区以及属性值,在CarPropertyManager中会将这些参数转化为一个CarPropertyValue对象继承往CarService转达。
mHvacManager.getIntProperty(CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone);private final CarPropertyManager mCarPropertyMgr;public int getIntProperty(int propertyId, int area) { return this.mCarPropertyMgr.getIntProperty(propertyId, area);}CarHvacManager也是通过注册一个callback来得到 Car API 的数据回调。
mHvacManager.registerCallback(mHardwareCallback);private final CarHvacManager.CarHvacEventCallback mHardwareCallback = new CarHvacManager.CarHvacEventCallback() { @Override public void onChangeEvent(final CarPropertyValue val) { int areaId = val.getAreaId(); switch (val.getPropertyId()) { case CarHvacManager.ID_ZONED_AC_ON: handleAcStateUpdate(getValue(val)); break; case CarHvacManager.ID_ZONED_FAN_DIRECTION: handleFanPositionUpdate(areaId, getValue(val)); break; case CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT: handleFanSpeedUpdate(areaId, getValue(val)); break; case CarHvacManager.ID_ZONED_TEMP_SETPOINT: handleTempUpdate(val); break; case CarHvacManager.ID_WINDOW_DEFROSTER_ON: handleDefrosterUpdate(areaId, getValue(val)); break; case CarHvacManager.ID_ZONED_AIR_RECIRCULATION_ON: handleAirCirculationUpdate(getValue(val)); break; case CarHvacManager.ID_ZONED_SEAT_TEMP: handleSeatWarmerUpdate(areaId, getValue(val)); break; case CarHvacManager.ID_ZONED_AUTOMATIC_MODE_ON: handleAutoModeUpdate(getValue(val)); break; case CarHvacManager.ID_ZONED_HVAC_POWER_ON: handleHvacPowerOn(getValue(val)); break; default: if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Unhandled HVAC event, id: " + val.getPropertyId()); } } } @Override public void onErrorEvent(final int propertyId, final int zone) { }};Hvac中每个Property对应的寄义如下:
// 全局属性,只有一个ID_MIRROR_DEFROSTER_ON //视镜除雾ID_STEERING_WHEEL_HEAT //方向盘温度ID_OUTSIDE_AIR_TEMP //室外温度ID_TEMPERATURE_DISPLAY_UNITS //在利用的温度// 地区属性,可在差别地区设置ID_ZONED_TEMP_SETPOINT //用户设置的温度ID_ZONED_TEMP_ACTUAL //地区实际温度ID_ZONED_HVAC_POWER_ON //HVAC体系电源开关ID_ZONED_FAN_SPEED_SETPOINT //风扇设置的速率ID_ZONED_FAN_SPEED_RPM //风扇实际的速率ID_ZONED_FAN_DIRECTION_AVAILABLE //风扇可设置的方向ID_ZONED_FAN_DIRECTION //现在风扇设置的方向ID_ZONED_SEAT_TEMP //座椅温度ID_ZONED_AC_ON //空调开关ID_ZONED_AUTOMATIC_MODE_ON //HVAC主动模式开关ID_ZONED_AIR_RECIRCULATION_ON //氛围循环开关ID_ZONED_MAX_AC_ON //空调最大速率开关ID_ZONED_DUAL_ZONE_ON //双区模式开关ID_ZONED_MAX_DEFROST_ON //最大除雾开关ID_ZONED_HVAC_AUTO_RECIRC_ON //主动循环模式开关ID_WINDOW_DEFROSTER_ON //除雾模式开关利用Car API时务必必要注意,注册的callback是有大概会非常频仍的产生回调的,应用层必要先将数据存储在DataStore中举行过滤,才能更新到UI上。而且也不要及时的打印日记,否则大概会导致日记缓冲区EOF,也会严肃干扰别的历程的日记输出。
3.7 DataStore
DataStore 用于存储HvacController从 Car API 中获取的属性值。
用户利用IVI界面和利用硬按键,都会更新Hvac的干系属性。这两种差别的更新方式都是从差别的线程更新到当前状态。别的,在某些环境下,Hvac体系大概会发送卖弄的更新,因此这个类将所有内容更新管理归并,从而确保在用户看来应用步调的界面是正常的
@GuardedBy("mFanSpeed")private Integer mFanSpeed = 0;private static final long COALESCE_TIME_MS = 0L;public int getFanSpeed() { synchronized (mFanSpeed) { return mFanSpeed; }}// 仅用于主动 获取、设定 数据时更新speed数据。public void setFanSpeed(int speed) { synchronized (mFanSpeed) { mFanSpeed = speed; mLastFanSpeedSet = SystemClock.uptimeMillis(); }}// 从callback中得到数据时,由于数据大概会革新的很频仍,以是必要先判断时间戳,确定命据是否真的必要更新public boolean shouldPropagateFanSpeedUpdate(int zone, int speed) { // TODO:我们暂时忽略风扇速率地区,由于我们没有多地区车。 synchronized (mFanSpeed) { if (SystemClock.uptimeMillis() - mLastFanSpeedSet < COALESCE_TIME_MS) { return false; } mFanSpeed = speed; } return true;}在HvacController中我们从callback得到数据革新时,先通过DataStore判断以下是否必要更新数据,假如确实必要更新,再将更新后的数据回调给其他的UI控制器。
// HvacController.javaprivate final CarHvacManager.CarHvacEventCallback mHardwareCallback = new CarHvacManager.CarHvacEventCallback() { @Override public void onChangeEvent(final CarPropertyValue val) { int areaId = val.getAreaId(); switch (val.getPropertyId()) { case CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT: // 处理惩罚来自callback的数据 handleFanSpeedUpdate(areaId, getValue(val)); break; // ... 省略 default: if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Unhandled HVAC event, id: " + val.getPropertyId()); } } }};private void handleFanSpeedUpdate(int zone, int speed) { // 判断是否必要更新本地的数据 boolean shouldPropagate = mDataStore.shouldPropagateFanSpeedUpdate(zone, speed); if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Fan Speed Update, zone: " + zone + " speed: " + speed + " should propagate: " + shouldPropagate); } if (shouldPropagate) { // 将更新后的数据回调给各个UI控制器 synchronized (mCallbacks) { for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).onFanSpeedChange(speed); } } }}public void setFanSpeed(final int fanSpeed) { // 更新当前的数据 mDataStore.setFanSpeed(fanSpeed); final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() { int newFanSpeed; protected Void doInBackground(Void... unused) { if (mHvacManager != null) { int zone = SEAT_ALL; // Car specific workaround. try { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Setting fanspeed to: " + fanSpeed); } mHvacManager.setIntProperty( CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone, fanSpeed); newFanSpeed = mHvacManager.getIntProperty( CarHvacManager.ID_ZONED_FAN_SPEED_SETPOINT, zone); } catch (android.car.CarNotConnectedException e) { Log.e(TAG, "Car not connected in setFanSpeed"); } } return null; } }; task.execute();}4. 总结