HarmonyOS 5开发从入门到精通(十六):天气应用实战(下)

HarmonyOS 5开发从入门到精通(十六):天气应用实战(下)

本章将继续完善天气应用,重点实现服务卡片、跨设备数据同步和高级功能,打造一个功能完整的天气应用。

一、核心概念

1. 服务卡片与原子化服务

服务卡片是HarmonyOS的特色功能,允许应用核心功能以卡片形式展示在桌面,无需打开完整应用。原子化服务进一步将应用功能拆分为独立服务单元,实现跨设备无缝体验。

2. 分布式数据管理

HarmonyOS的分布式数据管理能力使应用能够在多个设备间同步数据。通过分布式数据库KVStore,天气数据可以在手机、平板、智慧屏等设备间实时同步。

二、关键API详解

1. 服务卡片开发

// 卡片配置文件
"forms": [
  {
    "name": "weather_widget",
    "description": "天气服务卡片",
    "src": "./ets/WeatherWidgetExtension/WeatherCard.ets",
    "window": { "designWidth": 360 },
    "updateEnabled": true,
    "scheduledUpdateTime": 30, // 30分钟定时更新
    "defaultDimension": "2 * 2",
    "supportDimensions": ["2 * 2", "2 * 4"]
  }
]

2. FormExtensionAbility

import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'

export default class WeatherWidgetExtension extends FormExtensionAbility {
  // 卡片创建时调用
  async onAddForm(want: Want): Promise<formBindingData.FormBindingData> {
    const weatherData = await this.fetchWeatherData()
    return formBindingData.createFormBindingData(weatherData)
  }
  
  // 卡片更新时调用
  async onUpdateForm(formId: string): Promise<void> {
    const newData = await this.fetchWeatherData()
    await formProvider.updateForm(formId, 
      formBindingData.createFormBindingData(newData))
  }
}

3. 分布式数据同步

import distributedKVStore from '@ohos.data.distributedKVStore'

// 创建分布式数据管理器
const kvManager = distributedKVStore.createKVManager({
  bundleName: 'com.example.weatherapp',
  userInfo: { userId: '0', userType: distributedKVStore.UserType.SAME_USER_ID }
})

// 获取KVStore实例
const kvStore = await kvManager.getKVStore('weather_store', {
  createIfMissing: true,
  autoSync: true, // 开启自动同步
  kvStoreType: distributedKVStore.KVStoreType.SINGLE_VERSION
})

4. 数据变化监听

// 订阅数据变更事件
kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, 
  (data) => {
    this.handleDataChange(data)
  }
)

// 处理数据变更
private handleDataChange(data: distributedKVStore.ChangeNotification) {
  if (data.insertEntries.length > 0) {
    const newWeather = JSON.parse(data.insertEntries[0].value.value)
    this.updateWeatherDisplay(newWeather)
  }
}

5. 地理位置服务

import geolocation from '@ohos.geolocation'

// 获取当前位置
async getCurrentLocation(): Promise<string> {
  try {
    const location = await geolocation.getCurrentLocation({
      timeout: 30000,
      coordinateType: geolocation.CoordinateType.WGS84
    })
    return `${location.longitude},${location.latitude}`
  } catch (error) {
    return '北京' // 默认城市
  }
}

6. 动画效果

// 天气图标旋转动画
@State rotateAngle: number = 0

Image(this.weatherIcon)
  .rotate({ angle: this.rotateAngle })
  .onClick(() => {
    animateTo({ duration: 1000 }, () => {
      this.rotateAngle = 360
      this.rotateAngle = 0
    })
  })

7. 本地存储

import preferences from '@ohos.data.preferences'

// 保存用户偏好设置
async saveUserPreferences(): Promise<void> {
  const prefs = await preferences.getPreferences(this.context, 'weather_prefs')
  await prefs.put('favoriteCity', this.currentCity)
  await prefs.flush()
}

8. 后台任务管理

import backgroundTaskManager from '@ohos.resourceschedule.backgroundTaskManager'

// 申请后台运行权限
async requestBackgroundPermission(): Promise<void> {
  try {
    await backgroundTaskManager.requestSuspendDelay()
  } catch (error) {
    console.error('后台权限申请失败')
  }
}

三、实战案例

完整代码实现

import distributedKVStore from '@ohos.data.distributedKVStore'
import FormExtensionAbility from '@ohos.app.form.FormExtensionAbility'
import formBindingData from '@ohos.app.form.formBindingData'

// 天气服务卡片Ability
@Entry
@Component
struct WeatherWidgetExtension extends FormExtensionAbility {
  @State currentWeather: WeatherData = new WeatherData()
  private kvStore: distributedKVStore.SingleKVStore | null = null
  
  async onAddForm(want: Want): Promise<formBindingData.FormBindingData> {
    await this.initDistributedData()
    await this.loadWeatherData()
    return this.createBindingData()
  }
  
  async onUpdateForm(formId: string): Promise<void> {
    await this.loadWeatherData()
    const bindingData = this.createBindingData()
    await formProvider.updateForm(formId, bindingData)
  }
  
  // 初始化分布式数据
  private async initDistributedData() {
    const kvManager = distributedKVStore.createKVManager({
      bundleName: 'com.example.weather',
      userInfo: { userId: '0', userType: distributedKVStore.UserType.SAME_USER_ID }
    })
    
    this.kvStore = await kvManager.getKVStore('weather_store', {
      createIfMissing: true,
      autoSync: true
    })
    
    // 监听数据变化
    this.kvStore.on('dataChange', distributedKVStore.SubscribeType.SUBSCRIBE_TYPE_ALL, 
      (data) => this.onDataChanged(data))
  }
  
  // 加载天气数据
  private async loadWeatherData(): Promise<void> {
    try {
      // 尝试从分布式存储获取数据
      const syncedData = await this.getSyncedData()
      if (syncedData) {
        this.currentWeather = syncedData
        return
      }
      
      // 从网络API获取数据
      const apiData = await this.fetchFromAPI()
      this.currentWeather = apiData
      
      // 同步到其他设备
      await this.syncData(apiData)
    } catch (error) {
      console.error('加载天气数据失败:', error)
    }
  }
  
  // 创建绑定数据
  private createBindingData(): formBindingData.FormBindingData {
    return formBindingData.createFormBindingData({
      city: this.currentWeather.city,
      temperature: `${this.currentWeather.temp}℃`,
      condition: this.currentWeather.condition,
      updateTime: this.formatTime(new Date())
    })
  }
  
  build() {
    Column() {
      // 城市名称
      Text(this.currentWeather.city)
        .fontSize(16)
        .fontColor(Color.White)
        .margin({ bottom: 8 })
      
      // 温度和天气状况
      Row() {
        Text(this.currentWeather.temp.toString())
          .fontSize(24)
          .fontColor(Color.White)
          .fontWeight(FontWeight.Bold)
        
        Text('℃')
          .fontSize(16)
          .fontColor(Color.White)
          .margin({ left: 2, top: 4 })
      }
      .margin({ bottom: 4 })
      
      Text(this.currentWeather.condition)
        .fontSize(14)
        .fontColor(Color.White)
      
      // 更新时间
      Text(this.formatTime(new Date()))
        .fontSize(12)
        .fontColor('#CCFFFFFF')
        .margin({ top: 8 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#4A90E2')
    .padding(16)
  }
}

// 增强型天气页面组件
@Component
struct EnhancedWeatherPage {
  @State currentWeather: WeatherData = new WeatherData()
  @State favoriteCities: string[] = []
  @State isConnected: boolean = false
  
  private syncService: WeatherSyncService = new WeatherSyncService()
  
  async aboutToAppear() {
    await this.syncService.initialize()
    this.isConnected = this.syncService.isConnected
    await this.loadFavoriteCities()
    await this.loadWeatherData()
  }
  
  build() {
    Column() {
      // 连接状态指示器
      this.buildConnectionStatus()
      
      // 多城市管理
      this.buildCityManager()
      
      // 增强型天气卡片
      EnhancedWeatherCard({ weatherData: this.currentWeather })
      
      // 天气预报列表
      WeatherForecastList()
    }
  }
  
  @Builder
  private buildConnectionStatus() {
    Row() {
      Circle()
        .width(8)
        .height(8)
        .fill(this.isConnected ? '#4CAF50' : '#FF6B6B')
        .margin({ right: 8 })
      
      Text(this.isConnected ? '多设备已连接' : '单设备模式')
        .fontSize(14)
        .fontColor('#666666')
    }
    .margin({ top: 10, bottom: 10 })
  }
  
  @Builder
  private buildCityManager() {
    Column() {
      Text('收藏城市')
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .margin({ bottom: 10 })
      
      Scroll() {
        Row() {
          ForEach(this.favoriteCities, (city: string) => {
            Text(city)
              .fontSize(14)
              .padding(8)
              .backgroundColor('#F0F0F0')
              .borderRadius(4)
              .margin({ right: 8 })
              .onClick(() => this.switchCity(city))
          })
        }
        .padding(10)
      }
    }
    .margin({ bottom: 20 })
  }
}

// 分布式天气同步服务
class WeatherSyncService {
  private kvStore: distributedKVStore.SingleKVStore | null = null
  public isConnected: boolean = false
  
  async initialize(): Promise<void> {
    const kvManager = distributedKVStore.createKVManager({
      bundleName: 'com.example.weather',
      userInfo: { userId: '0', userType: distributedKVStore.UserType.SAME_USER_ID }
    })
    
    this.kvStore = await kvManager.getKVStore('weather_sync', {
      createIfMissing: true,
      autoSync: true
    })
    
    this.isConnected = true
  }
  
  async syncWeatherData(weather: WeatherData): Promise<void> {
    if (!this.kvStore) return
    
    try {
      await this.kvStore.put('current_weather', JSON.stringify(weather))
    } catch (error) {
      console.error('数据同步失败:', error)
    }
  }
}

四、总结

关键知识点

  • 服务卡片的生命周期管理和定时更新机制
  • 分布式数据同步的实现原理和跨设备通信
  • 多城市管理和用户偏好设置的本地存储
  • 天气应用的性能优化和用户体验提升

🔧 核心API列表

  • FormExtensionAbility- 服务卡片基础能力
  • distributedKVStore- 分布式数据存储管理
  • formBindingData.createFormBindingData()- 卡片数据绑定
  • backgroundTaskManager- 后台任务管理
  • preferences- 本地偏好设置存储
  • animateTo- 属性动画实现

💡 应用建议

  1. 服务卡片更新频率要合理平衡,避免频繁更新耗电
  2. 分布式数据同步要考虑网络状况,实现优雅降级
  3. 多设备适配时要考虑不同屏幕尺寸的布局优化
  4. 建议实现数据缓存策略,提升离线使用体验

通过本章学习,你已经掌握了HarmonyOS天气应用的完整开发流程。下一章我们将开始新闻阅读应用的实战开发。

posted @ 2025-12-23 21:27  奇崽  阅读(0)  评论(0)    收藏  举报