HarmonyOS跨设备位置开发实践

举报
Jack20 发表于 2026/06/20 00:06:08 2026/06/20
【摘要】 HarmonyOS跨设备位置开发实践 什么原理你有没有遇到过这种场景:在车里用手机导航,但手机GPS信号不好,想用车载GPS获得更精准的定位;或者在室内用平板,想用手机的位置信息来显示附近的商店;又或者在户外运动时,想用手表的GPS记录轨迹,但用手机查看实时位置。这就是分布式位置服务要解决的问题——让位置信息可以跨设备共享,一个设备可以使用另一个设备的定位能力。不同设备的定位能力差异很大。...

HarmonyOS跨设备位置开发实践

什么原理

你有没有遇到过这种场景:在车里用手机导航,但手机GPS信号不好,想用车载GPS获得更精准的定位;或者在室内用平板,想用手机的位置信息来显示附近的商店;又或者在户外运动时,想用手表的GPS记录轨迹,但用手机查看实时位置。

这就是分布式位置服务要解决的问题——让位置信息可以跨设备共享,一个设备可以使用另一个设备的定位能力。

不同设备的定位能力差异很大。手机通常有GPS、Wi-Fi定位、基站定位等多种方式;车载设备可能有更专业的GPS模块;手表可能只有基础的GPS;而电视可能完全没有定位能力。分布式位置服务打破了这种限制,让应用可以使用"整个超级终端"上最优的定位能力。

核心原理

分布式位置服务架构

分布式位置服务基于以下几个核心概念:

  1. LocationProvider:位置提供者,声明本设备的定位能力
  2. LocationProxy:位置代理,在远程设备上代表定位服务
  3. LocationFusion:位置融合,整合多个来源的位置信息
  4. GeofenceManager:地理围栏管理,支持跨设备地理围栏
flowchart TB
    classDef primary fill:#4A90E2,stroke:#2E5C8A,stroke-width:2px,color:#fff
    classDef warning fill:#F5A623,stroke:#D4821C,stroke-width:2px,color:#fff
    classDef error fill:#E74C3C,stroke:#C0392B,stroke-width:2px,color:#fff
    classDef info fill:#7B68EE,stroke:#5A4FCF,stroke-width:2px,color:#fff

    subgraph Providers[位置提供者]
        A1[GPS]:::primary --> A2[LocationProvider]:::info
        A3[Wi-Fi定位]:::primary --> A2
        A4[基站定位]:::primary --> A2
        A5[蓝牙定位]:::primary --> A2
    end

    subgraph Fusion[位置融合]
        B1[数据收集]:::info --> B2[质量评估]:::warning
        B2 --> B3[加权融合]:::error
        B3 --> B4[结果输出]:::primary
    end

    subgraph Consumer[位置使用者]
        C1[LocationProxy]:::primary --> C2[位置缓存]:::info
        C2 --> C3[应用回调]:::warning
        C3 --> C4[地理围栏]:::primary
    end

    A2 --> B1
    B4 --> C1

定位方式分类

分布式位置服务支持多种定位方式:

1. GPS定位:高精度,但耗电大、室内不可用
2. Wi-Fi定位:室内可用,精度中等
3. 基站定位:覆盖广,但精度低
4. 蓝牙定位:室内高精度,需要信标
5. 融合定位:结合多种方式,平衡精度和功耗

位置质量评估

不同来源的位置信息质量不同,需要评估和选择:

// 位置质量评估参数
interface LocationQuality {
    // 精度(米)
    accuracy: number;
    
    // 定位方式
    locationType: 'gps' | 'wifi' | 'cell' | 'ble' | 'fused';
    
    // 时间戳
    timestamp: number;
    
    // 置信度(0-1)
    confidence: number;
    
    // 卫星数量(仅GPS)
    satelliteCount?: number;
}

代码实战

示例一:基础分布式位置服务

这是最基础的分布式位置服务示例,使用远程设备的GPS获取位置。

// DistributedLocation.ets
import geoLocationManager from '@ohos.geoLocationManager';
import deviceManager from '@ohos.distributedDeviceManager';
import distributedLocation from '@ohos.distributedLocation';

export class DistributedLocation {
    private context: common.UIAbilityContext;
    private deviceManager: deviceManager.DeviceManager | null = null;
    
    // 本地位置订阅
    private localLocationCallback: geoLocationManager.LocationCallback | null = null;
    
    // 远程位置代理
    private remoteLocationProxy: distributedLocation.LocationProxy | null = null;
    
    // 位置状态
    private locationState: LocationState = {
        currentLocation: null,
        isLocating: false,
        lastUpdateTime: 0
    };

    constructor(context: common.UIAbilityContext) {
        this.context = context;
    }

    // 初始化
    async initialize(): Promise<void> {
        console.info('[DistributedLocation] Initializing...');
        
        try {
            // 初始化设备管理器
            this.deviceManager = deviceManager.createDeviceManager(
                this.context.applicationInfo.name
            );
            
            // 发现位置提供者
            await this.discoverLocationProviders();
            
            console.info('[DistributedLocation] Initialized');
        } catch (error) {
            console.error('[DistributedLocation] Init failed:', error);
        }
    }

    // 发现位置提供者
    async discoverLocationProviders(): Promise<LocationProviderInfo[]> {
        console.info('[DistributedLocation] Discovering location providers');
        
        const providers: LocationProviderInfo[] = [];
        
        // 1. 检查本地定位能力
        const localCapabilities = await this.getLocalLocationCapabilities();
        if (localCapabilities.hasGps || localCapabilities.hasNetworkLocation) {
            providers.push({
                providerId: 'local',
                providerName: '本机定位',
                deviceId: 'local',
                deviceName: '本机',
                isLocal: true,
                capabilities: localCapabilities
            });
        }
        
        // 2. 检查远程设备定位能力
        if (this.deviceManager) {
            const devices = this.deviceManager.getTrustedDeviceListSync();
            
            for (const device of devices) {
                if (device.deviceState !== deviceManager.DeviceState.ONLINE) {
                    continue;
                }
                
                try {
                    const remoteCapabilities = await distributedLocation.queryLocationCapabilities(
                        device.deviceId
                    );
                    
                    if (remoteCapabilities.hasGps || remoteCapabilities.hasNetworkLocation) {
                        providers.push({
                            providerId: device.deviceId,
                            providerName: `${device.deviceName}定位`,
                            deviceId: device.deviceId,
                            deviceName: device.deviceName,
                            isLocal: false,
                            capabilities: remoteCapabilities
                        });
                    }
                } catch (error) {
                    console.error('[DistributedLocation] Query failed:', device.deviceId, error);
                }
            }
        }
        
        console.info('[DistributedLocation] Found', providers.length, 'providers');
        return providers;
    }

    // 获取本地定位能力
    private async getLocalLocationCapabilities(): Promise<LocationCapabilities> {
        try {
            const locationManager = geoLocationManager.getLocationManager(this.context);
            const providers = locationManager.getProviders();
            
            return {
                hasGps: providers.includes('gps'),
                hasNetworkLocation: providers.includes('network'),
                hasPassiveLocation: providers.includes('passive'),
                supportsGeofence: true,
                supportsHighAccuracy: true
            };
        } catch (error) {
            console.error('[DistributedLocation] Get local capabilities failed:', error);
            return {
                hasGps: false,
                hasNetworkLocation: false,
                hasPassiveLocation: false,
                supportsGeofence: false,
                supportsHighAccuracy: false
            };
        }
    }

    // 请求位置(自动选择最佳提供者)
    async requestLocation(options?: LocationRequestOptions): Promise<LocationInfo | null> {
        console.info('[DistributedLocation] Requesting location');
        
        // 选择最佳位置提供者
        const bestProvider = await this.selectBestProvider(options);
        
        if (!bestProvider) {
            console.error('[DistributedLocation] No available provider');
            return null;
        }
        
        console.info('[DistributedLocation] Using provider:', bestProvider.providerId);
        
        // 根据提供者类型请求位置
        if (bestProvider.isLocal) {
            return await this.requestLocalLocation(options);
        } else {
            return await this.requestRemoteLocation(bestProvider.deviceId, options);
        }
    }

    // 选择最佳位置提供者
    private async selectBestProvider(
        options?: LocationRequestOptions
    ): Promise<LocationProviderInfo | null> {
        const providers = await this.discoverLocationProviders();
        
        if (providers.length === 0) return null;
        
        // 评分选择
        let bestProvider: LocationProviderInfo | null = null;
        let bestScore = -1;
        
        for (const provider of providers) {
            const score = this.evaluateProvider(provider, options);
            
            if (score > bestScore) {
                bestScore = score;
                bestProvider = provider;
            }
        }
        
        return bestProvider;
    }

    // 评估位置提供者
    private evaluateProvider(
        provider: LocationProviderInfo, 
        options?: LocationRequestOptions
    ): number {
        let score = 0;
        
        // GPS优先
        if (provider.capabilities.hasGps) {
            score += 50;
        }
        
        // 网络定位次之
        if (provider.capabilities.hasNetworkLocation) {
            score += 30;
        }
        
        // 高精度需求
        if (options?.priority === 'high_accuracy' && provider.capabilities.hasGps) {
            score += 20;
        }
        
        // 低功耗需求
        if (options?.priority === 'low_power' && !provider.capabilities.hasGps) {
            score += 20;
        }
        
        // 本地优先(减少延迟)
        if (provider.isLocal) {
            score += 10;
        }
        
        return score;
    }

    // 请求本地位置
    private async requestLocalLocation(
        options?: LocationRequestOptions
    ): Promise<LocationInfo | null> {
        console.info('[DistributedLocation] Requesting local location');
        
        try {
            const locationManager = geoLocationManager.getLocationManager(this.context);
            
            // 构建请求配置
            const requestConfig: geoLocationManager.LocationRequest = {
                priority: this.mapPriority(options?.priority),
                scenario: this.mapScenario(options?.scenario),
                timeInterval: options?.updateInterval || 0,
                distanceInterval: options?.minDistance || 0,
                maxAccuracy: options?.maxAccuracy || 0
            };
            
            // 获取单次位置
            const location = await locationManager.getCurrentLocation(requestConfig);
            
            return this.convertLocation(location, 'local');
        } catch (error) {
            console.error('[DistributedLocation] Local location failed:', error);
            return null;
        }
    }

    // 请求远程位置
    private async requestRemoteLocation(
        deviceId: string,
        options?: LocationRequestOptions
    ): Promise<LocationInfo | null> {
        console.info('[DistributedLocation] Requesting remote location from:', deviceId);
        
        try {
            // 创建或获取位置代理
            if (!this.remoteLocationProxy) {
                this.remoteLocationProxy = await distributedLocation.createLocationProxy({
                    sourceDeviceId: deviceId
                });
            }
            
            // 请求位置
            const location = await this.remoteLocationProxy.getCurrentLocation({
                priority: options?.priority || 'balanced',
                maxAccuracy: options?.maxAccuracy
            });
            
            return this.convertLocation(location, deviceId);
        } catch (error) {
            console.error('[DistributedLocation] Remote location failed:', error);
            return null;
        }
    }

    // 持续监听位置变化
    async startLocationUpdates(
        callback: (location: LocationInfo) => void,
        options?: LocationRequestOptions
    ): Promise<boolean> {
        console.info('[DistributedLocation] Starting location updates');
        
        // 选择最佳提供者
        const bestProvider = await this.selectBestProvider(options);
        
        if (!bestProvider) {
            console.error('[DistributedLocation] No available provider');
            return false;
        }
        
        if (bestProvider.isLocal) {
            return await this.startLocalLocationUpdates(callback, options);
        } else {
            return await this.startRemoteLocationUpdates(
                bestProvider.deviceId, 
                callback, 
                options
            );
        }
    }

    // 开始本地位置更新
    private async startLocalLocationUpdates(
        callback: (location: LocationInfo) => void,
        options?: LocationRequestOptions
    ): Promise<boolean> {
        try {
            const locationManager = geoLocationManager.getLocationManager(this.context);
            
            // 创建回调
            this.localLocationCallback = {
                onLocationResult: (result: geoLocationManager.LocationResult) => {
                    if (result.locations && result.locations.length > 0) {
                        const location = this.convertLocation(result.locations[0], 'local');
                        callback(location);
                        
                        this.locationState.currentLocation = location;
                        this.locationState.lastUpdateTime = Date.now();
                    }
                }
            };
            
            // 构建请求配置
            const requestConfig: geoLocationManager.LocationRequest = {
                priority: this.mapPriority(options?.priority),
                scenario: this.mapScenario(options?.scenario),
                timeInterval: options?.updateInterval || 1000,
                distanceInterval: options?.minDistance || 0
            };
            
            // 开始监听
            locationManager.on('locationChange', requestConfig, this.localLocationCallback);
            
            this.locationState.isLocating = true;
            console.info('[DistributedLocation] Local updates started');
            return true;
        } catch (error) {
            console.error('[DistributedLocation] Start local updates failed:', error);
            return false;
        }
    }

    // 开始远程位置更新
    private async startRemoteLocationUpdates(
        deviceId: string,
        callback: (location: LocationInfo) => void,
        options?: LocationRequestOptions
    ): Promise<boolean> {
        try {
            // 创建位置代理
            this.remoteLocationProxy = await distributedLocation.createLocationProxy({
                sourceDeviceId: deviceId
            });
            
            // 订阅位置变化
            await this.remoteLocationProxy.subscribeLocationChange({
                priority: options?.priority || 'balanced',
                updateInterval: options?.updateInterval || 1000
            }, (location) => {
                const converted = this.convertLocation(location, deviceId);
                callback(converted);
                
                this.locationState.currentLocation = converted;
                this.locationState.lastUpdateTime = Date.now();
            });
            
            this.locationState.isLocating = true;
            console.info('[DistributedLocation] Remote updates started');
            return true;
        } catch (error) {
            console.error('[DistributedLocation] Start remote updates failed:', error);
            return false;
        }
    }

    // 停止位置更新
    async stopLocationUpdates(): Promise<void> {
        console.info('[DistributedLocation] Stopping location updates');
        
        // 停止本地监听
        if (this.localLocationCallback) {
            const locationManager = geoLocationManager.getLocationManager(this.context);
            locationManager.off('locationChange', this.localLocationCallback);
            this.localLocationCallback = null;
        }
        
        // 停止远程监听
        if (this.remoteLocationProxy) {
            await this.remoteLocationProxy.unsubscribeLocationChange();
        }
        
        this.locationState.isLocating = false;
    }

    // 获取最后已知位置
    async getLastKnownLocation(): Promise<LocationInfo | null> {
        // 先检查缓存
        if (this.locationState.currentLocation) {
            const elapsed = Date.now() - this.locationState.lastUpdateTime;
            if (elapsed < 30000) {  // 30秒内有效
                return this.locationState.currentLocation;
            }
        }
        
        // 尝试获取新位置
        return await this.requestLocation();
    }

    // 地理编码(坐标转地址)
    async reverseGeocode(location: LocationInfo): Promise<AddressInfo | null> {
        console.info('[DistributedLocation] Reverse geocoding');
        
        try {
            const locationManager = geoLocationManager.getLocationManager(this.context);
            
            const request: geoLocationManager.ReverseGeocodingRequest = {
                latitude: location.latitude,
                longitude: location.longitude,
                maxItems: 1
            };
            
            const result = await locationManager.getAddressesFromLocation(request);
            
            if (result && result.length > 0) {
                return this.convertAddress(result[0]);
            }
            
            return null;
        } catch (error) {
            console.error('[DistributedLocation] Reverse geocode failed:', error);
            return null;
        }
    }

    // 地理编码(地址转坐标)
    async geocode(address: string): Promise<LocationInfo | null> {
        console.info('[DistributedLocation] Geocoding:', address);
        
        try {
            const locationManager = geoLocationManager.getLocationManager(this.context);
            
            const request: geoLocationManager.GeocodingRequest = {
                address: address,
                maxItems: 1
            };
            
            const result = await locationManager.getLocationFromAddress(request);
            
            if (result && result.length > 0) {
                return {
                    latitude: result[0].latitude,
                    longitude: result[0].longitude,
                    accuracy: 0,
                    timestamp: Date.now(),
                    source: 'geocode'
                };
            }
            
            return null;
        } catch (error) {
            console.error('[DistributedLocation] Geocode failed:', error);
            return null;
        }
    }

    // 辅助方法
    private mapPriority(priority?: string): geoLocationManager.LocationRequestPriority {
        const priorityMap: Record<string, geoLocationManager.LocationRequestPriority> = {
            'high_accuracy': geoLocationManager.LocationRequestPriority.PRIORITY_ACCURACY,
            'balanced': geoLocationManager.LocationRequestPriority.PRIORITY_BALANCED_POWER_ACCURACY,
            'low_power': geoLocationManager.LocationRequestPriority.PRIORITY_LOW_POWER
        };
        return priorityMap[priority || 'balanced'] || 
            geoLocationManager.LocationRequestPriority.PRIORITY_BALANCED_POWER_ACCURACY;
    }

    private mapScenario(scenario?: string): geoLocationManager.LocationRequestScenario {
        const scenarioMap: Record<string, geoLocationManager.LocationRequestScenario> = {
            'navigation': geoLocationManager.LocationRequestScenario.SCENARIO_NAVIGATION,
            'tracking': geoLocationManager.LocationRequestScenario.SCENARIO_POSITION_SHARING,
            'fitness': geoLocationManager.LocationRequestScenario.SCENARIO_SPORTS_FITNESS
        };
        return scenarioMap[scenario || 'tracking'] || 
            geoLocationManager.LocationRequestScenario.SCENARIO_POSITION_SHARING;
    }

    private convertLocation(
        location: geoLocationManager.Location, 
        source: string
    ): LocationInfo {
        return {
            latitude: location.latitude,
            longitude: location.longitude,
            altitude: location.altitude,
            accuracy: location.accuracy,
            speed: location.speed,
            direction: location.direction,
            timestamp: location.timeStamp,
            source: source
        };
    }

    private convertAddress(address: geoLocationManager.Address): AddressInfo {
        return {
            formattedAddress: address.formattedAddress,
            country: address.countryName,
            province: address.administrativeArea,
            city: address.locality,
            district: address.subLocality,
            street: address.thoroughfare,
            postalCode: address.postalCode
        };
    }

    // 释放资源
    async release(): Promise<void> {
        console.info('[DistributedLocation] Releasing');
        
        // 停止位置更新
        await this.stopLocationUpdates();
        
        // 释放远程代理
        if (this.remoteLocationProxy) {
            await this.remoteLocationProxy.release();
            this.remoteLocationProxy = null;
        }
        
        // 释放设备管理器
        if (this.deviceManager) {
            deviceManager.releaseDeviceManager(this.deviceManager);
            this.deviceManager = null;
        }
    }
}

// 接口定义
interface LocationState {
    currentLocation: LocationInfo | null;
    isLocating: boolean;
    lastUpdateTime: number;
}

interface LocationProviderInfo {
    providerId: string;
    providerName: string;
    deviceId: string;
    deviceName: string;
    isLocal: boolean;
    capabilities: LocationCapabilities;
}

interface LocationCapabilities {
    hasGps: boolean;
    hasNetworkLocation: boolean;
    hasPassiveLocation: boolean;
    supportsGeofence: boolean;
    supportsHighAccuracy: boolean;
}

interface LocationRequestOptions {
    priority?: 'high_accuracy' | 'balanced' | 'low_power';
    scenario?: 'navigation' | 'tracking' | 'fitness';
    updateInterval?: number;
    minDistance?: number;
    maxAccuracy?: number;
}

interface LocationInfo {
    latitude: number;
    longitude: number;
    altitude?: number;
    accuracy: number;
    speed?: number;
    direction?: number;
    timestamp: number;
    source: string;
}

interface AddressInfo {
    formattedAddress: string;
    country?: string;
    province?: string;
    city?: string;
    district?: string;
    street?: string;
    postalCode?: string;
}

示例二:多源位置融合

融合多个设备的位置信息,提高定位精度和可靠性。

// LocationFusion.ets
import distributedLocation from '@ohos.distributedLocation';

export class LocationFusion {
    // 位置源管理
    private locationSources: Map<string, LocationSource> = new Map();
    
    // 融合算法
    private fusionAlgorithm: FusionAlgorithm | null = null;
    
    // 融合结果
    private fusedLocation: LocationInfo | null = null;

    // 添加位置源
    async addLocationSource(
        sourceId: string, 
        config: LocationSourceConfig
    ): Promise<void> {
        console.info('[LocationFusion] Adding source:', sourceId);
        
        // 创建位置代理
        const proxy = await distributedLocation.createLocationProxy({
            sourceDeviceId: config.deviceId
        });
        
        // 订阅位置变化
        await proxy.subscribeLocationChange({
            priority: config.priority || 'balanced'
        }, (location) => {
            this.handleLocationUpdate(sourceId, location);
        });
        
        // 保存源信息
        this.locationSources.set(sourceId, {
            id: sourceId,
            config: config,
            proxy: proxy,
            lastLocation: null,
            weight: config.weight || 1.0
        });
    }

    // 处理位置更新
    private handleLocationUpdate(sourceId: string, location: LocationInfo): void {
        // 更新源的最后位置
        const source = this.locationSources.get(sourceId);
        if (source) {
            source.lastLocation = location;
        }
        
        // 执行融合
        this.performFusion();
    }

    // 执行位置融合
    private performFusion(): void {
        // 收集所有有效位置
        const validLocations: WeightedLocation[] = [];
        
        const now = Date.now();
        
        for (const source of this.locationSources.values()) {
            if (source.lastLocation) {
                const elapsed = now - source.lastLocation.timestamp;
                
                // 只使用最近10秒的位置
                if (elapsed < 10000) {
                    // 计算权重(考虑精度和时效性)
                    const accuracyWeight = 1 / (source.lastLocation.accuracy + 1);
                    const timeWeight = Math.exp(-elapsed / 5000);  // 时间衰减
                    const finalWeight = source.weight * accuracyWeight * timeWeight;
                    
                    validLocations.push({
                        location: source.lastLocation,
                        weight: finalWeight
                    });
                }
            }
        }
        
        if (validLocations.length === 0) return;
        
        // 执行融合算法
        if (this.fusionAlgorithm) {
            this.fusedLocation = this.fusionAlgorithm.fuse(validLocations);
        } else {
            // 默认:加权平均
            this.fusedLocation = this.weightedAverage(validLocations);
        }
        
        // 通知融合结果
        this.notifyFusedLocation();
    }

    // 加权平均融合
    private weightedAverage(locations: WeightedLocation[]): LocationInfo {
        let totalWeight = 0;
        let weightedLat = 0;
        let weightedLon = 0;
        let weightedAlt = 0;
        
        for (const wl of locations) {
            totalWeight += wl.weight;
            weightedLat += wl.location.latitude * wl.weight;
            weightedLon += wl.location.longitude * wl.weight;
            if (wl.location.altitude) {
                weightedAlt += wl.location.altitude * wl.weight;
            }
        }
        
        // 计算融合精度
        const avgAccuracy = locations.reduce((sum, wl) => 
            sum + wl.location.accuracy, 0) / locations.length;
        
        return {
            latitude: weightedLat / totalWeight,
            longitude: weightedLon / totalWeight,
            altitude: weightedAlt > 0 ? weightedAlt / totalWeight : undefined,
            accuracy: avgAccuracy * 0.7,  // 融合后精度提高
            timestamp: Date.now(),
            source: 'fused'
        };
    }

    // 设置融合算法
    setFusionAlgorithm(algorithm: FusionAlgorithm): void {
        this.fusionAlgorithm = algorithm;
    }

    // 获取融合位置
    getFusedLocation(): LocationInfo | null {
        return this.fusedLocation;
    }

    // 通知融合位置
    private notifyFusedLocation(): void {
        if (this.fusedLocation) {
            AppStorage.setOrCreate('fusedLocation', this.fusedLocation);
        }
    }
}

// 卡尔曼滤波融合算法
class KalmanFusion implements FusionAlgorithm {
    // 状态:[lat, lon, v_lat, v_lon]
    private state: number[] = [0, 0, 0, 0];
    
    // 协方差矩阵
    private P: number[][] = [
        [1, 0, 0, 0],
        [0, 1, 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 1]
    ];
    
    // 过程噪声
    private Q: number = 0.01;
    
    // 是否已初始化
    private initialized: boolean = false;

    fuse(locations: WeightedLocation[]): LocationInfo {
        if (locations.length === 0) {
            return this.stateToLocation();
        }
        
        // 初始化
        if (!this.initialized) {
            this.initialize(locations[0].location);
            this.initialized = true;
        }
        
        // 预测步骤
        this.predict();
        
        // 更新步骤(使用所有测量)
        for (const wl of locations) {
            this.update(wl.location, wl.weight);
        }
        
        return this.stateToLocation();
    }

    private initialize(location: LocationInfo): void {
        this.state = [
            location.latitude,
            location.longitude,
            0,
            0
        ];
    }

    private predict(): void {
        // 简化的预测:状态保持不变,协方差增加
        for (let i = 0; i < 4; i++) {
            this.P[i][i] += this.Q;
        }
    }

    private update(location: LocationInfo, weight: number): void {
        // 测量噪声(精度越高噪声越小)
        const R = location.accuracy * location.accuracy / weight;
        
        // 卡尔曼增益
        const K_lat = this.P[0][0] / (this.P[0][0] + R);
        const K_lon = this.P[1][1] / (this.P[1][1] + R);
        
        // 更新状态
        this.state[0] += K_lat * (location.latitude - this.state[0]);
        this.state[1] += K_lon * (location.longitude - this.state[1]);
        
        // 更新协方差
        this.P[0][0] *= (1 - K_lat);
        this.P[1][1] *= (1 - K_lon);
    }

    private stateToLocation(): LocationInfo {
        return {
            latitude: this.state[0],
            longitude: this.state[1],
            accuracy: Math.sqrt(this.P[0][0] + this.P[1][1]),
            timestamp: Date.now(),
            source: 'kalman_fused'
        };
    }
}

// 接口定义
interface LocationSource {
    id: string;
    config: LocationSourceConfig;
    proxy: distributedLocation.LocationProxy;
    lastLocation: LocationInfo | null;
    weight: number;
}

interface LocationSourceConfig {
    deviceId: string;
    priority?: string;
    weight?: number;
}

interface WeightedLocation {
    location: LocationInfo;
    weight: number;
}

interface FusionAlgorithm {
    fuse(locations: WeightedLocation[]): LocationInfo;
}

示例三:跨设备地理围栏

支持跨设备的地理围栏监控。

// DistributedGeofence.ets
import distributedLocation from '@ohos.distributedLocation';
import geoLocationManager from '@ohos.geoLocationManager';

export class DistributedGeofence {
    // 地理围栏管理器
    private geofenceManager: geoLocationManager.GeofenceManager | null = null;
    
    // 围栏定义
    private geofences: Map<string, GeofenceDefinition> = new Map();
    
    // 围栏状态
    private geofenceStates: Map<string, GeofenceState> = new Map();
    
    // 围栏事件回调
    private eventCallback: ((event: GeofenceEvent) => void) | null = null;

    // 初始化
    async initialize(): Promise<void> {
        console.info('[DistributedGeofence] Initializing');
        
        this.geofenceManager = geoLocationManager.getGeofenceManager(this.context);
        
        // 监听围栏事件
        this.geofenceManager.on('geofenceResult', (result: geoLocationManager.GeofenceResult) => {
            this.handleGeofenceResult(result);
        });
    }

    // 添加地理围栏
    async addGeofence(definition: GeofenceDefinition): Promise<string> {
        console.info('[DistributedGeofence] Adding geofence:', definition.name);
        
        // 生成围栏ID
        const geofenceId = `geofence_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
        
        // 创建围栏请求
        const request: geoLocationManager.GeofenceRequest = {
            geofences: [{
                latitude: definition.center.latitude,
                longitude: definition.center.longitude,
                radius: definition.radius,
                expiration: definition.expiration || 0,
                loiteringDelay: definition.loiteringDelay || 0
            }],
            transitionTypes: this.mapTransitionTypes(definition.transitionTypes)
        };
        
        // 添加围栏
        await this.geofenceManager?.addGeofence(request);
        
        // 保存围栏定义
        this.geofences.set(geofenceId, {
            ...definition,
            id: geofenceId
        });
        
        // 初始化状态
        this.geofenceStates.set(geofenceId, {
            geofenceId: geofenceId,
            currentState: 'unknown',
            lastTransitionTime: 0,
            dwellTime: 0
        });
        
        console.info('[DistributedGeofence] Geofence added:', geofenceId);
        return geofenceId;
    }

    // 添加跨设备地理围栏
    async addDistributedGeofence(
        definition: GeofenceDefinition,
        targetDeviceId: string
    ): Promise<string> {
        console.info('[DistributedGeofence] Adding distributed geofence for device:', targetDeviceId);
        
        // 创建远程位置代理
        const locationProxy = await distributedLocation.createLocationProxy({
            sourceDeviceId: targetDeviceId
        });
        
        // 订阅位置变化
        await locationProxy.subscribeLocationChange({
            priority: 'balanced'
        }, (location) => {
            this.checkGeofenceForDevice(definition, location, targetDeviceId);
        });
        
        // 保存围栏(包含设备信息)
        const geofenceId = `dist_geofence_${Date.now()}`;
        
        this.geofences.set(geofenceId, {
            ...definition,
            id: geofenceId,
            targetDeviceId: targetDeviceId,
            locationProxy: locationProxy
        });
        
        return geofenceId;
    }

    // 检查设备是否在围栏内
    private checkGeofenceForDevice(
        definition: GeofenceDefinition,
        location: LocationInfo,
        deviceId: string
    ): void {
        // 计算距离
        const distance = this.calculateDistance(
            location.latitude, location.longitude,
            definition.center.latitude, definition.center.longitude
        );
        
        const wasInside = this.geofenceStates.get(definition.id || '')?.currentState === 'inside';
        const isInside = distance <= definition.radius;
        
        // 状态转换
        if (!wasInside && isInside) {
            // 进入围栏
            this.emitGeofenceEvent({
                type: 'enter',
                geofenceId: definition.id || '',
                geofenceName: definition.name,
                deviceId: deviceId,
                location: location,
                timestamp: Date.now()
            });
        } else if (wasInside && !isInside) {
            // 离开围栏
            this.emitGeofenceEvent({
                type: 'exit',
                geofenceId: definition.id || '',
                geofenceName: definition.name,
                deviceId: deviceId,
                location: location,
                timestamp: Date.now()
            });
        }
        
        // 更新状态
        if (definition.id) {
            this.geofenceStates.set(definition.id, {
                geofenceId: definition.id,
                currentState: isInside ? 'inside' : 'outside',
                lastTransitionTime: Date.now(),
                dwellTime: 0
            });
        }
    }

    // 处理围栏结果
    private handleGeofenceResult(result: geoLocationManager.GeofenceResult): void {
        console.info('[DistributedGeofence] Geofence result:', result.transition);
        
        const geofenceId = result.geofenceId.toString();
        const definition = this.geofences.get(geofenceId);
        
        if (!definition) return;
        
        // 转换事件类型
        const eventType = this.mapTransitionToEvent(result.transition);
        
        // 发送事件
        this.emitGeofenceEvent({
            type: eventType,
            geofenceId: geofenceId,
            geofenceName: definition.name,
            deviceId: 'local',
            timestamp: Date.now()
        });
        
        // 更新状态
        const state = this.geofenceStates.get(geofenceId);
        if (state) {
            state.currentState = eventType === 'enter' || eventType === 'dwell' ? 'inside' : 'outside';
            state.lastTransitionTime = Date.now();
        }
    }

    // 移除地理围栏
    async removeGeofence(geofenceId: string): Promise<void> {
        console.info('[DistributedGeofence] Removing geofence:', geofenceId);
        
        const definition = this.geofences.get(geofenceId);
        
        if (definition) {
            // 如果是跨设备围栏,释放代理
            if (definition.locationProxy) {
                await definition.locationProxy.unsubscribeLocationChange();
                await definition.locationProxy.release();
            }
            
            // 如果是本地围栏,从管理器移除
            if (!definition.targetDeviceId) {
                await this.geofenceManager?.removeGeofence(parseInt(geofenceId));
            }
            
            // 移除定义和状态
            this.geofences.delete(geofenceId);
            this.geofenceStates.delete(geofenceId);
        }
    }

    // 获取围栏状态
    getGeofenceState(geofenceId: string): GeofenceState | null {
        return this.geofenceStates.get(geofenceId) || null;
    }

    // 设置事件回调
    setEventCallback(callback: (event: GeofenceEvent) => void): void {
        this.eventCallback = callback;
    }

    // 发送围栏事件
    private emitGeofenceEvent(event: GeofenceEvent): void {
        console.info('[DistributedGeofence] Event:', event.type, event.geofenceName);
        
        if (this.eventCallback) {
            this.eventCallback(event);
        }
        
        // 通知AppStorage
        AppStorage.setOrCreate('geofenceEvent', event);
    }

    // 计算两点距离(Haversine公式)
    private calculateDistance(
        lat1: number, lon1: number,
        lat2: number, lon2: number
    ): number {
        const R = 6371000;  // 地球半径(米)
        
        const dLat = this.toRad(lat2 - lat1);
        const dLon = this.toRad(lon2 - lon1);
        
        const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) *
            Math.sin(dLon / 2) * Math.sin(dLon / 2);
        
        const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        
        return R * c;
    }

    private toRad(deg: number): number {
        return deg * Math.PI / 180;
    }

    // 辅助方法
    private mapTransitionTypes(types: string[]): number {
        let result = 0;
        if (types.includes('enter')) {
            result |= geoLocationManager.TransitionType.ENTER_GEOFENCE_CONVERSION;
        }
        if (types.includes('exit')) {
            result |= geoLocationManager.TransitionType.EXIT_GEOFENCE_CONVERSION;
        }
        if (types.includes('dwell')) {
            result |= geoLocationManager.TransitionType.DWELL_GEOFENCE_CONVERSION;
        }
        return result;
    }

    private mapTransitionToEvent(transition: number): 'enter' | 'exit' | 'dwell' {
        if (transition === geoLocationManager.TransitionType.ENTER_GEOFENCE_CONVERSION) {
            return 'enter';
        } else if (transition === geoLocationManager.TransitionType.EXIT_GEOFENCE_CONVERSION) {
            return 'exit';
        } else {
            return 'dwell';
        }
    }
}

// 接口定义
interface GeofenceDefinition {
    id?: string;
    name: string;
    center: { latitude: number; longitude: number };
    radius: number;
    expiration?: number;
    loiteringDelay?: number;
    transitionTypes: ('enter' | 'exit' | 'dwell')[];
    targetDeviceId?: string;
    locationProxy?: distributedLocation.LocationProxy;
}

interface GeofenceState {
    geofenceId: string;
    currentState: 'inside' | 'outside' | 'unknown';
    lastTransitionTime: number;
    dwellTime: number;
}

interface GeofenceEvent {
    type: 'enter' | 'exit' | 'dwell';
    geofenceId: string;
    geofenceName: string;
    deviceId: string;
    location?: LocationInfo;
    timestamp: number;
}

踰坑与注意事项

坑一:位置漂移

问题描述:静止时位置数据仍有小幅跳动,导致围栏误触发。

解决方案:使用平滑滤波

// 位置平滑器
class LocationSmoother {
    private history: LocationInfo[] = [];
    private maxHistory: number = 5;
    
    smooth(location: LocationInfo): LocationInfo {
        this.history.push(location);
        
        if (this.history.length > this.maxHistory) {
            this.history.shift();
        }
        
        // 计算移动平均
        const avgLat = this.history.reduce((sum, l) => sum + l.latitude, 0) / this.history.length;
        const avgLon = this.history.reduce((sum, l) => sum + l.longitude, 0) / this.history.length;
        
        return {
            ...location,
            latitude: avgLat,
            longitude: avgLon
        };
    }
}

坑二:室内定位精度低

问题描述:室内GPS信号弱,定位精度大幅下降。

解决方案:使用融合定位和室内定位技术

// 室内定位增强
class IndoorLocationEnhancer {
    // 检测是否在室内
    isIndoor(location: LocationInfo): boolean {
        // GPS精度大于50米可能是室内
        return location.accuracy > 50;
    }
    
    // 获取增强位置
    async getEnhancedLocation(location: LocationInfo): Promise<LocationInfo> {
        if (this.isIndoor(location)) {
            // 尝试使用Wi-Fi定位
            const wifiLocation = await this.getWifiLocation();
            if (wifiLocation && wifiLocation.accuracy < location.accuracy) {
                return wifiLocation;
            }
            
            // 尝试使用蓝牙信标定位
            const bleLocation = await this.getBleLocation();
            if (bleLocation && bleLocation.accuracy < location.accuracy) {
                return bleLocation;
            }
        }
        
        return location;
    }
    
    private async getWifiLocation(): Promise<LocationInfo | null> {
        // 实际实现需要Wi-Fi定位逻辑
        return null;
    }
    
    private async getBleLocation(): Promise<LocationInfo | null> {
        // 实际实现需要蓝牙信标定位逻辑
        return null;
    }
}

坑三:位置权限问题

问题描述:跨设备位置访问涉及权限,需要正确处理。

解决方案:检查和请求权限

// 位置权限检查
async function checkLocationPermission(context: common.UIAbilityContext): Promise<boolean> {
    const permission = 'ohos.permission.LOCATION';
    
    try {
        const result = await abilityAccessCtrl.verifyAccessToken(
            context.applicationInfo.accessTokenId,
            permission
        );
        
        return result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;
    } catch (error) {
        console.error('Permission check failed:', error);
        return false;
    }
}

// 请求位置权限
async function requestLocationPermission(context: common.UIAbilityContext): Promise<boolean> {
    const permission = 'ohos.permission.LOCATION';
    
    try {
        const result = await abilityAccessCtrl.requestPermissionsFromUser(context, [permission]);
        return result.authResults[0] === 0;
    } catch (error) {
        console.error('Permission request failed:', error);
        return false;
    }
}

HarmonyOS 6适配

新增高精度定位模式

// 高精度定位配置
const highAccuracyConfig: distributedLocation.HighAccuracyConfig = {
    enabled: true,
    
    // 使用RTK(实时动态差分)
    rtk: {
        enabled: true,
        correctionSource: 'network'  // 网络差分源
    },
    
    // 使用A-GPS辅助
    agps: {
        enabled: true,
        serverUrl: 'https://agps.example.com'
    },
    
    // 目标精度
    targetAccuracy: 1  // 1米
};

await locationProxy.enableHighAccuracy(highAccuracyConfig);

增强的位置元数据

// 获取详细位置信息
const detailedLocation = await locationProxy.getDetailedLocation();

console.info('Latitude:', detailedLocation.latitude);
console.info('Longitude:', detailedLocation.longitude);
console.info('Altitude:', detailedLocation.altitude);
console.info('Accuracy:', detailedLocation.accuracy);
console.info('Speed:', detailedLocation.speed);
console.info('Direction:', detailedLocation.direction);
console.info('Satellites used:', detailedLocation.satellitesUsed);
console.info('Satellites in view:', detailedLocation.satellitesInView);
console.info('HDOP:', detailedLocation.hdop);  // 水平精度因子
console.info('VDOP:', detailedLocation.vdop);  // 垂直精度因子
console.info('PDOP:', detailedLocation.pdop);  // 位置精度因子
console.info('Location type:', detailedLocation.locationType);
console.info('Network type:', detailedLocation.networkType);

位置历史记录

// 获取位置历史
const history = await distributedLocation.getLocationHistory({
    startTime: Date.now() - 24 * 60 * 60 * 1000,  // 最近24小时
    endTime: Date.now(),
    maxCount: 1000
});

console.info('Location history count:', history.length);

// 分析轨迹
const analysis = analyzeTrack(history);
console.info('Total distance:', analysis.totalDistance, 'm');
console.info('Average speed:', analysis.avgSpeed, 'm/s');
console.info('Max speed:', analysis.maxSpeed, 'm/s');

总结

分布式位置服务让定位能力突破了设备的物理限制,一个设备可以使用整个"超级终端"上最优的定位能力。无论是使用远程GPS、融合多源位置,还是跨设备地理围栏,都为应用提供了更精准、更可靠的位置服务。

核心要点

  1. 位置提供者发现:自动发现本地和远程定位能力
  2. 智能选择策略:根据需求选择最佳位置提供者
  3. 多源位置融合:整合多个来源提高精度和可靠性
  4. 跨设备地理围栏:支持监控远程设备的位置状态

最佳实践

  • 使用融合定位平衡精度和功耗
  • 实现位置平滑减少漂移影响
  • 室内场景使用Wi-Fi/蓝牙定位增强
  • 正确处理位置权限
  • 善用地理围栏实现智能场景触发
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。