
鸿蒙跨端天气同步系统:多设备实时天气数据共享 原创
鸿蒙跨端天气同步系统:多设备实时天气数据共享
本文将基于HarmonyOS 5的分布式能力和ArkUI框架,实现一个支持多设备同步的天气查询应用,能够在不同设备间实时同步天气数据和城市偏好。
技术架构
数据获取层:调用公开天气API获取实时数据
数据处理层:解析和转换API返回的天气数据
分布式同步层:通过分布式数据管理实现多设备数据共享
UI展示层:响应式UI展示天气信息和同步状态
完整代码实现
天气数据模型定义
// model/WeatherData.ts
export class WeatherData {
city: string = ‘’; // 城市名称
temperature: number = 0; // 当前温度(℃)
weather: string = ‘’; // 天气状况
humidity: number = 0; // 湿度(%)
windSpeed: number = 0; // 风速(km/h)
airQuality: string = ‘’; // 空气质量
forecast: DailyForecast[] = []; // 天气预报
updateTime: number = 0; // 更新时间戳
deviceId: string = ‘’; // 最后更新的设备ID
constructor(data?: Partial<WeatherData>) {
if (data) {
Object.assign(this, data);
if (!this.updateTime) {
this.updateTime = Date.now();
}
}
export class DailyForecast {
date: string = ‘’; // 日期
dayWeather: string = ‘’; // 白天天气
nightWeather: string = ‘’; // 夜间天气
maxTemp: number = 0; // 最高温度
minTemp: number = 0; // 最低温度
天气API服务实现
// service/WeatherApiService.ts
import http from ‘@ohos.net.http’;
import { WeatherData, DailyForecast } from ‘…/model/WeatherData’;
const WEATHER_API_KEY = ‘YOUR_API_KEY’; // 替换为实际API Key
const API_URL = ‘https://api.openweathermap.org/data/2.5/weather’;
export class WeatherApiService {
private httpRequest = http.createHttp();
// 获取城市天气数据
async fetchWeatherData(city: string): Promise<WeatherData> {
return new Promise((resolve, reject) => {
this.httpRequest.request(
{API_URL}?q={city}&units=metric&appid=${WEATHER_API_KEY},
method: ‘GET’,
header: { 'Content-Type': 'application/json' }
},
(err, data) => {
if (err) {
reject(err);
return;
if (data.responseCode === 200) {
const result = JSON.parse(data.result);
const weather = this.parseWeatherData(result);
resolve(weather);
else {
reject(new Error(请求失败,状态码: ${data.responseCode}));
}
);
});
// 解析API返回的天气数据
private parseWeatherData(data: any): WeatherData {
return new WeatherData({
city: data.name,
temperature: Math.round(data.main.temp),
weather: data.weather[0].main,
humidity: data.main.humidity,
windSpeed: data.wind.speed,
airQuality: this.getAirQuality(data.main.aqi),
forecast: this.parseForecast(data.forecast)
});
// 解析天气预报数据
private parseForecast(forecastData: any[]): DailyForecast[] {
return forecastData.slice(0, 5).map(item => ({
date: item.dt_txt,
dayWeather: item.weather[0].main,
nightWeather: item.weather[0].main, // 简化处理
maxTemp: Math.round(item.main.temp_max),
minTemp: Math.round(item.main.temp_min)
}));
// 获取空气质量描述
private getAirQuality(aqi: number): string {
if (aqi <= 50) return ‘优’;
if (aqi <= 100) return ‘良’;
if (aqi <= 150) return ‘轻度污染’;
if (aqi <= 200) return ‘中度污染’;
return ‘重度污染’;
}
分布式天气同步服务
// service/WeatherSyncService.ts
import distributedData from ‘@ohos.data.distributedData’;
import deviceInfo from ‘@ohos.deviceInfo’;
import { WeatherData } from ‘…/model/WeatherData’;
const STORE_ID = ‘weather_sync_store’;
const WEATHER_KEY = ‘current_weather’;
export class WeatherSyncService {
private kvManager: distributedData.KVManager;
private kvStore: distributedData.SingleKVStore;
private localDeviceId: string = deviceInfo.deviceId;
// 初始化分布式数据存储
async initialize() {
const config = {
bundleName: ‘com.example.weatherapp’,
userInfo: {
userId: ‘weather_user’,
userType: distributedData.UserType.SAME_USER_ID
};
this.kvManager = distributedData.createKVManager(config);
const options = {
createIfMissing: true,
encrypt: false,
backup: false,
autoSync: true,
kvStoreType: distributedData.KVStoreType.SINGLE_VERSION
};
this.kvStore = await this.kvManager.getKVStore(STORE_ID, options);
// 订阅数据变更
this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_ALL, (data) => {
this.handleDataChange(data);
});
// 处理数据变更
private handleDataChange(data: distributedData.ChangeNotification) {
if (data.insertEntries.length > 0 && data.insertEntries[0].key === WEATHER_KEY) {
const newWeather = JSON.parse(data.insertEntries[0].value.value);
AppStorage.setOrCreate(‘currentWeather’, new WeatherData(newWeather));
}
// 同步天气数据到所有设备
async syncWeatherData(weather: WeatherData) {
weather.deviceId = this.localDeviceId;
await this.kvStore.put(WEATHER_KEY, JSON.stringify(weather));
// 获取当前设备ID
getLocalDeviceId(): string {
return this.localDeviceId;
}
天气卡片组件实现
// components/WeatherCard.ets
import { WeatherData } from ‘…/model/WeatherData’;
@Component
export struct WeatherCard {
@Link currentWeather: WeatherData;
@State isExpanded: boolean = false;
build() {
Column() {
// 当前天气概览
Row() {
Column() {
Text(this.currentWeather.city)
.fontSize(20)
.fontWeight(FontWeight.Bold)
Text(this.formatTime(this.currentWeather.updateTime))
.fontSize(12)
.fontColor('#888888')
.layoutWeight(1)
Text(${this.currentWeather.temperature}°)
.fontSize(48)
.fontWeight(FontWeight.Bold)
.width(‘100%’)
.padding(16)
// 天气详情
if (this.isExpanded) {
Column() {
Divider()
// 天气状况
Row() {
WeatherDetailItem({
icon: $r('app.media.ic_weather'),
label: '天气',
value: this.currentWeather.weather
})
WeatherDetailItem({
icon: $r('app.media.ic_humidity'),
label: '湿度',
value: ${this.currentWeather.humidity}%
})
.margin({ top: 12, bottom: 12 })
// 风速和空气质量
Row() {
WeatherDetailItem({
icon: $r('app.media.ic_wind'),
label: '风速',
value: ${this.currentWeather.windSpeed}km/h
})
WeatherDetailItem({
icon: $r('app.media.ic_air'),
label: '空气质量',
value: this.currentWeather.airQuality
})
// 天气预报
Text('未来5天预报')
.fontSize(16)
.fontWeight(FontWeight.Bold)
.margin({ top: 20, bottom: 8 })
ForEach(this.currentWeather.forecast, (item: DailyForecast) => {
ForecastItem({ forecast: item })
})
.padding(16)
// 展开/收起按钮
Row() {
Image($r('app.media.ic_arrow'))
.width(20)
.height(20)
.rotate({ angle: this.isExpanded ? 180 : 0 })
Text(this.isExpanded ? '收起' : '查看更多')
.fontSize(14)
.margin({ left: 4 })
.justifyContent(FlexAlign.Center)
.width('100%')
.padding(8)
.onClick(() => {
animateTo({ duration: 300 }, () => {
this.isExpanded = !this.isExpanded;
});
})
.width(‘100%’)
.backgroundColor('#FFFFFF')
.borderRadius(12)
.shadow({ radius: 4, color: '#10000000', offsetX: 0, offsetY: 2 })
// 格式化时间显示
private formatTime(timestamp: number): string {
const date = new Date(timestamp);
return {date.getHours().toString().padStart(2, ‘0’)}:{date.getMinutes().toString().padStart(2, ‘0’)} 更新;
}
@Component
struct WeatherDetailItem {
private icon: Resource;
private label: string;
private value: string;
build() {
Row() {
Image(this.icon)
.width(24)
.height(24)
.margin({ right: 8 })
Column() {
Text(this.label)
.fontSize(12)
.fontColor('#888888')
Text(this.value)
.fontSize(16)
.fontWeight(FontWeight.Medium)
}
.layoutWeight(1)
}
@Component
struct ForecastItem {
private forecast: DailyForecast;
build() {
Row() {
Text(this.forecast.date)
.fontSize(14)
.layoutWeight(1)
Row() {
Text(${this.forecast.maxTemp}°)
.fontSize(14)
.fontColor('#FF5722')
Text(${this.forecast.minTemp}°)
.fontSize(14)
.fontColor('#2196F3')
.margin({ left: 8 })
Text(this.forecast.dayWeather)
.fontSize(14)
.margin({ left: 16 })
.padding(8)
.width('100%')
}
天气主页面实现
// pages/WeatherPage.ets
import { WeatherApiService } from ‘…/service/WeatherApiService’;
import { WeatherSyncService } from ‘…/service/WeatherSyncService’;
import { WeatherCard } from ‘…/components/WeatherCard’;
@Entry
@Component
struct WeatherPage {
private apiService: WeatherApiService = new WeatherApiService();
private syncService: WeatherSyncService = new WeatherSyncService();
@StorageLink(‘currentWeather’) currentWeather: WeatherData = new WeatherData();
@State cityInput: string = ‘北京’;
@State isLoading: boolean = false;
@State isSynced: boolean = false;
async aboutToAppear() {
await this.syncService.initialize();
this.isSynced = true;
await this.loadWeatherData(this.cityInput);
build() {
Column() {
// 城市搜索栏
Row() {
TextInput({ text: this.cityInput, placeholder: '输入城市名称' })
.layoutWeight(1)
.onSubmit((value: string) => {
if (value) {
this.loadWeatherData(value);
})
Button('搜索')
.margin({ left: 8 })
.onClick(() => {
if (this.cityInput) {
this.loadWeatherData(this.cityInput);
})
.width(‘90%’)
.margin({ top: 20, bottom: 16 })
// 加载状态
if (this.isLoading) {
LoadingProgress()
.width(50)
.height(50)
.margin({ top: 100 })
else if (this.currentWeather.city) {
// 天气卡片
WeatherCard({ currentWeather: $currentWeather })
.width('90%')
.margin({ bottom: 20 })
// 同步状态
Row() {
Circle()
.width(10)
.height(10)
.fill(this.isSynced ? '#4CAF50' : '#F44336')
.margin({ right: 5 })
Text(this.isSynced ? '已连接多设备' : '单机模式')
.fontSize(14)
.fontColor('#666666')
}
.width(‘100%’)
.height('100%')
// 加载天气数据
private async loadWeatherData(city: string) {
this.isLoading = true;
try {
const weather = await this.apiService.fetchWeatherData(city);
this.currentWeather = weather;
await this.syncService.syncWeatherData(weather);
catch (err) {
prompt.showToast({ message: '获取天气数据失败', duration: 2000 });
finally {
this.isLoading = false;
}
实现原理详解
数据获取与同步流程:
主设备调用天气API获取最新数据
数据解析后同步到分布式数据库
其他设备通过订阅机制接收更新
所有设备保持天气数据一致
UI响应机制:
使用@StorageLink绑定天气数据
数据变更自动触发UI更新
展开/收起动画使用animateTo实现平滑过渡
设备协同原理:
每个设备都可以作为数据源更新天气
显示最后更新设备的ID
冲突时以最后更新的数据为准
扩展功能建议
多城市管理:
// 添加多城市支持
async addFavoriteCity(city: string) {
await this.kvStore.put(city_${city}, JSON.stringify({
city,
lastUpdate: Date.now()
}));
天气预警通知:
// 检查极端天气条件
function checkWeatherAlert(weather: WeatherData) {
if (weather.weather = ‘暴雨’ || weather.airQuality = ‘重度污染’) {
showNotification(‘天气预警’, 当前{weather.city}有{weather.weather}天气,请注意安全);
}
天气趋势图表:
// 使用图表库展示温度变化趋势
import charts from ‘@ohos.charts’;
function renderTemperatureChart(forecast: DailyForecast[]) {
const data = forecast.map(item => ({
date: item.date,
temp: item.maxTemp
}));
// 渲染折线图…
总结
本文展示了如何利用HarmonyOS的分布式能力构建一个多设备同步的天气应用。通过将天气数据存储在分布式数据库中,实现了多设备间的实时数据同步,确保用户在不同设备上都能获取一致的天气信息。
这种架构不仅适用于天气应用,也可以扩展到新闻、等需要实时数据同步的场景。合理利用鸿蒙的分布式能力,可以大大简化多设备协同开发的复杂度,提升用户体验。
