4. 实现车辆属性批量读取
任务说明
在Android Car服务中,CarPropertyManager是访问车辆属性的核心API。本案例将实现一个工具类,通过CarPropertyManager.getProperty批量读取多个车辆属性(如车速、燃油量、总里程),并统一处理异步返回结果。最终通过一个简单的控制台调用验证读取流程是否打通。
环境与依赖
- Android 10+(API 29+)设备或模拟器,支持CarService
- 项目需引入
android.car系统库(非SDK公开API,需系统签名或CarApp权限) - 权限声明:
<uses-permission android:name="android.car.permission.CAR_SPEED" />(按需添加对应属性权限)
核心实现
import android.car.Car;
import android.car.CarPropertyManager;
import android.car.VehiclePropertyIds;
import android.car.VehiclePropertyValues;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 车辆属性批量读取工具
* 支持同时请求多个属性,通过回调统一返回结果
*/
public class PropertyBatchReader {
private static final String TAG = "PropertyBatchReader";
private final CarPropertyManager mCarPropertyManager;
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
// 属性ID与请求码映射(请求码用于区分同一属性的多次请求)
private final Map<Integer, Integer> mRequestCodes = new HashMap<>();
private int mNextRequestCode = 0;
public interface BatchCallback {
/**
* @param successMap 成功获取的属性ID -> 属性值
* @param failedIds 获取失败的属性ID列表
*/
void onResult(Map<Integer, Object> successMap, List<Integer> failedIds);
}
public PropertyBatchReader(Context context) {
Car car = Car.createCar(context);
mCarPropertyManager = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE);
}
/**
* 批量读取属性
* @param propertyIds 要读取的属性ID数组(如VehiclePropertyIds.PERF_VEHICLE_SPEED)
* @param callback 结果回调(主线程执行)
*/
public void readProperties(int[] propertyIds, BatchCallback callback) {
if (propertyIds == null || propertyIds.length == 0) {
callback.onResult(new HashMap<>(), new ArrayList<>());
return;
}
Map<Integer, Object> successMap = new HashMap<>();
List<Integer> failedIds = new ArrayList<>();
final int total = propertyIds.length;
final int[] pending = {total};
for (int propertyId : propertyIds) {
int requestCode = mNextRequestCode++;
mRequestCodes.put(requestCode, propertyId);
try {
mCarPropertyManager.getProperty(propertyId, requestCode);
} catch (Exception e) {
Log.e(TAG, "请求属性失败: " + propertyId, e);
failedIds.add(propertyId);
if (--pending[0] == 0) {
deliverResult(successMap, failedIds, callback);
}
}
}
// 注册属性变化监听(用于接收getProperty的异步结果)
mCarPropertyManager.registerPropertyCallback(mPropertyCallback, propertyIds);
}
private final CarPropertyManager.CarPropertyEventCallback mPropertyCallback =
new CarPropertyManager.CarPropertyEventCallback() {
@Override
public void onChangeEvent(CarPropertyValue value) {
int propertyId = value.getPropertyId();
int requestCode = value.getRequestCode();
// 只处理我们发起的请求
Integer expectedId = mRequestCodes.get(requestCode);
if (expectedId == null || expectedId != propertyId) {
return;
}
mRequestCodes.remove(requestCode);
successMap.put(propertyId, value.getValue());
if (--pending[0] == 0) {
unregisterCallback();
deliverResult(successMap, failedIds, callback);
}
}
@Override
public void onErrorEvent(int propertyId, int status) {
Log.e(TAG, "属性读取错误: " + propertyId + ", status=" + status);
failedIds.add(propertyId);
if (--pending[0] == 0) {
unregisterCallback();
deliverResult(successMap, failedIds, callback);
}
}
};
private void unregisterCallback() {
try {
mCarPropertyManager.unregisterPropertyCallback(mPropertyCallback);
} catch (Exception ignored) {}
}
private void deliverResult(Map<Integer, Object> successMap,
List<Integer> failedIds,
BatchCallback callback) {
mMainHandler.post(() -> callback.onResult(successMap, failedIds));
}
}
调用示例
// 在Activity或Service中调用
PropertyBatchReader reader = new PropertyBatchReader(context);
int[] properties = {
VehiclePropertyIds.PERF_VEHICLE_SPEED, // 车速 km/h
VehiclePropertyIds.FUEL_LEVEL, // 燃油量 0-100
VehiclePropertyIds.ODOMETER // 总里程 km
};
reader.readProperties(properties, (successMap, failedIds) -> {
if (successMap.containsKey(VehiclePropertyIds.PERF_VEHICLE_SPEED)) {
float speed = (float) successMap.get(VehiclePropertyIds.PERF_VEHICLE_SPEED);
Log.d("BatchDemo", "当前车速: " + speed + " km/h");
}
if (successMap.containsKey(VehiclePropertyIds.FUEL_LEVEL)) {
float fuel = (float) successMap.get(VehiclePropertyIds.FUEL_LEVEL);
Log.d("BatchDemo", "燃油量: " + fuel + "%");
}
if (successMap.containsKey(VehiclePropertyIds.ODOMETER)) {
long odometer = (long) successMap.get(VehiclePropertyIds.ODOMETER);
Log.d("BatchDemo", "总里程: " + odometer + " km");
}
if (!failedIds.isEmpty()) {
Log.w("BatchDemo", "读取失败属性: " + failedIds);
}
});
验证方法
- 在支持CarService的模拟器或真机上部署应用(需系统签名)
- 运行后观察Logcat中
BatchDemo标签的输出 - 通过条件:能打印出车速、燃油量、里程三项数据(模拟器可能返回默认值或0),且无
读取失败属性日志
关键说明
getProperty是异步请求,结果通过CarPropertyEventCallback.onChangeEvent返回,因此必须注册监听才能收到数据- 不同属性的返回值类型不同:车速和燃油量为
Float,里程为Long,需按属性类型强制转换 - 若某属性无权限或硬件不支持,会进入
onErrorEvent回调,计入失败列表 - 实际项目中需在
onDestroy中释放Car连接,本示例聚焦批量读取逻辑,未包含完整生命周期管理