HarmonyOS跨设备位置开发实践
HarmonyOS跨设备位置开发实践
什么原理
你有没有遇到过这种场景:在车里用手机导航,但手机GPS信号不好,想用车载GPS获得更精准的定位;或者在室内用平板,想用手机的位置信息来显示附近的商店;又或者在户外运动时,想用手表的GPS记录轨迹,但用手机查看实时位置。
这就是分布式位置服务要解决的问题——让位置信息可以跨设备共享,一个设备可以使用另一个设备的定位能力。
不同设备的定位能力差异很大。手机通常有GPS、Wi-Fi定位、基站定位等多种方式;车载设备可能有更专业的GPS模块;手表可能只有基础的GPS;而电视可能完全没有定位能力。分布式位置服务打破了这种限制,让应用可以使用"整个超级终端"上最优的定位能力。
核心原理
分布式位置服务架构
分布式位置服务基于以下几个核心概念:
- LocationProvider:位置提供者,声明本设备的定位能力
- LocationProxy:位置代理,在远程设备上代表定位服务
- LocationFusion:位置融合,整合多个来源的位置信息
- 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、融合多源位置,还是跨设备地理围栏,都为应用提供了更精准、更可靠的位置服务。
核心要点:
- 位置提供者发现:自动发现本地和远程定位能力
- 智能选择策略:根据需求选择最佳位置提供者
- 多源位置融合:整合多个来源提高精度和可靠性
- 跨设备地理围栏:支持监控远程设备的位置状态
最佳实践:
- 使用融合定位平衡精度和功耗
- 实现位置平滑减少漂移影响
- 室内场景使用Wi-Fi/蓝牙定位增强
- 正确处理位置权限
- 善用地理围栏实现智能场景触发
- 点赞
- 收藏
- 关注作者
评论(0)