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);
    }
});

验证方法

  1. 在支持CarService的模拟器或真机上部署应用(需系统签名)
  2. 运行后观察Logcat中BatchDemo标签的输出
  3. 通过条件:能打印出车速、燃油量、里程三项数据(模拟器可能返回默认值或0),且无读取失败属性日志

关键说明

  • getProperty是异步请求,结果通过CarPropertyEventCallback.onChangeEvent返回,因此必须注册监听才能收到数据
  • 不同属性的返回值类型不同:车速和燃油量为Float,里程为Long,需按属性类型强制转换
  • 若某属性无权限或硬件不支持,会进入onErrorEvent回调,计入失败列表
  • 实际项目中需在onDestroy中释放Car连接,本示例聚焦批量读取逻辑,未包含完整生命周期管理