HarmonyOS NEXT实战:WinkWeatherPulse天气应用完整开发记录

一、项目简介

WinkWeatherPulse是一款基于HarmonyOS NEXT(ArkTS)开发的沉浸式天气应用,主打"多源数据融合 + AI天气助手 + 动态视觉体验"。不同于普通天气App只调用单一数据源,本项目同时接入和风天气和高德地图两路API,通过字段级合并算法实现数据互补;同时内置DeepSeek和通义千问双AI模型,提供每日天气简报、预警解读、生活指数建议等智能服务。

项目从0到1完整实现了天气查询、城市管理、地图选点、AI对话、桌面小组件、预警通知等功能,代码分层清晰,适合作为HarmonyOS NEXT开发的实战参考。

二、功能一览

功能模块说明
实时天气温度、体感、湿度、风向风力、气压、能见度
多日预报3天/7天/15天天气预报,逐小时预报
空气质量AQI指数、污染物详情、空气质量等级
生活指数穿衣、紫外线、运动、洗车等指数
天气预警灾害性天气预警推送,后台轮询检测
AI天气助手每日简报、预警解读、自由对话,支持DeepSeek/通义千问切换
城市管理多城市添加切换,地图选点定位
动态背景根据天气类型切换渐变色+Canvas粒子动画(雨/雪/晴/雾)
桌面小组件2x2、2x4、4x4三种尺寸天气卡片
自定义图表温度趋势曲线、AQI柱状图、风力罗盘

三、技术栈

类别技术
开发语言ArkTS(TypeScript超集)
系统HarmonyOS NEXT
开发工具DevEco Studio
状态管理AppStorage + @StorageProp + @ObservedV2
天气API和风天气(QWeather)+ 高德地图(AMap)
AI模型DeepSeek V4 Flash + 通义千问 Qwen3.6 Max
网络请求封装HttpClient,支持超时重试、gzip压缩
动画渲染Canvas自定义粒子系统
桌面卡片Form Extension Ability
数据存储Preferences持久化

四、项目结构

entry/src/main/ets/
├── api/                    # API层
│   ├── ApiConfig.ets       # 全局API配置(密钥、端点、超时)
│   ├── amap/               # 高德地图API(地理编码、天气)
│   └── qweather/           # 和风天气API(实况、预报、空气、预警)
├── component/              # 自定义组件
│   ├── DynamicBackground.ets    # 动态沉浸式背景
│   ├── BottomTabBar.ets         # 底部导航栏
│   ├── WeatherCard.ets          # 天气卡片
│   ├── TempCurveChart.ets       # 温度趋势曲线
│   ├── AqiBarChart.ets          # AQI柱状图
│   ├── WindCompass.ets          # 风力罗盘
│   ├── WeatherIcon.ets          # 天气图标映射
│   └── ChatBubble.ets           # AI聊天气泡
├── model/                  # 数据模型
│   ├── MergedWeather.ets        # 合并后的天气数据模型
│   ├── MergeStrategy.ets        # 合并策略定义
│   ├── WeatherTypes.ets         # 天气类型枚举
│   ├── City.ets                 # 城市模型
│   └── AiConversation.ets       # AI对话模型
├── pages/                  # 页面
│   ├── Index.ets               # 主页(Tab容器)
│   ├── WeatherHome.ets         # 天气主页
│   ├── AiAssistantPage.ets     # AI助手页
│   ├── CityManagePage.ets      # 城市管理
│   ├── CitySelectMapPage.ets   # 地图选城
│   ├── LoginPage.ets           # 登录页
│   ├── MinePage.ets            # 我的页面
│   ├── SettingsPage.ets        # 设置页
│   └── RadarPage.ets           # 雷达图页
├── service/                # 服务层
│   ├── WeatherRepository.ets   # 天气数据仓库(单例)
│   ├── WeatherMergeService.ets # 双源合并调度服务
│   ├── FieldMerger.ets         # 字段级合并器
│   ├── LocationService.ets     # 定位服务
│   ├── WarningPollService.ets  # 预警轮询服务
│   ├── NotificationManager.ets # 通知管理
│   ├── BackgroundTaskManager.ets # 后台任务管理
│   └── ai/                     # AI服务
│       ├── AiService.ets           # AI服务门面
│       ├── OpenAiCompatibleClient.ets # OpenAI兼容客户端
│       ├── PromptTemplates.ets     # 场景化Prompt模板
│       └── WeatherContextBuilder.ets # 天气上下文构建
├── widget/                 # 桌面小组件
│   └── pages/
│       ├── Widget2x2.ets
│       ├── Widget2x4.ets
│       └── Widget4x4.ets
└── utils/                  # 工具类
    ├── HttpClient.ets          # 网络请求封装
    ├── Logger.ets              # 日志工具
    ├── PreferencesStorage.ets  # 持久化存储
    └── StreamParser.ets        # 流式解析(AI SSE)

五、核心功能实现

5.1 双源天气数据合并

这是本项目的核心亮点之一。不做简单的"主源失败切备源",而是双向并发拉取 + 字段级互补合并

实现思路:

  1. 并发请求和风天气(基于经纬度)和高德天气(基于adcode)
  2. 使用Promise.allSettled保证任一源失败不影响另一源
  3. FieldMerger逐字段执行primary/fallback/tolerance合并策略
  4. 若位置信息缺少adcode,先调用高德逆地理编码获取

关键代码:

// WeatherMergeService.ets 核心片段
async fetchAndMerge(loc: LocationRef, cityName?: string): Promise<MergedWeather> {
  // 1. 若无adcode,先逆地理编码
  let adcode = loc.adcode ?? null;
  if (!adcode) {
    const regeo = await amapGeoService.reverseGeocode(loc.lat, loc.lng);
    adcode = regeo?.adcode;
  }

  // 2. 并发拉取双源,allSettled保证不抛异常
  const results = await Promise.allSettled([
    qweatherService.fetchAll(loc),
    amapWeatherService.fetchAll(adcode),
  ]);

  const qw = results[0].status === 'fulfilled' ? results[0].value : null;
  const amap = results[1].status === 'fulfilled' ? results[1].value : null;

  // 3. 字段级合并
  return this.merger.merge(qw, amap);
}

合并策略示例:

  • 实时温度:以和风为主,高德为备,差值超过2℃时取均值
  • 空气质量:以和风为主(和风AQI数据更全)
  • 风力风向:两源都有则取和风,缺失则用高德
  • 预警信息:两源合并去重

5.2 AI天气助手

AI助手支持双模型切换,内置4种场景化Prompt,支持流式输出(SSE)。

架构设计:

  • AiService作为门面,统一对外接口
  • OpenAiCompatibleClient封装OpenAI兼容协议,DeepSeek和通义千问都走这套
  • PromptTemplates定义每日简报、预警解读、生活指数、自由对话4种system prompt
  • WeatherContextBuilder动态注入当前天气数据作为上下文

关键代码:

// AiService.ets - 每日简报快捷方法
async dailyBriefing(weather: MergedWeather): Promise<string> {
  const context = WeatherContextBuilder.build(weather);
  const prompt = PromptTemplates.dailyBriefing(context);
  return this.client.chatCompletion([
    { role: 'system', content: prompt },
    { role: 'user', content: '请生成今日天气简报' }
  ], { stream: true });
}

AI配置:

// ApiConfig.ets
export const AI_CONFIG = {
  deepseek: {
    baseUrl: 'https://api.deepseek.com/v1',
    model: 'deepseek-v4-flash',
    displayName: 'DeepSeek V4 Flash',
  },
  qwen: {
    baseUrl: 'https://xxx.aliyuncs.com/compatible-mode/v1',
    model: 'qwen3.6-max-preview',
    displayName: 'Qwen3.6 Max',
  },
  defaultTemperature: 0.7,
  defaultMaxTokens: 800,
};

5.3 动态沉浸式背景

根据天气类型和昼夜状态,动态切换渐变色 + Canvas粒子动画。

实现要点:

  • 9种天气大类(晴/多云/阴/雨/大雨/雷暴/雪/雾/霾)各有日间/夜间两套渐变色
  • 粒子系统基于Canvas定时绘制,避免大量DOM节点
  • @ObservedV2 + @Trace实现状态驱动的背景刷新

粒子类型:

type ParticleType = 'none' | 'rain' | 'snow' | 'sun' | 'fog' | 'haze';

// 渐变色板示例
const GRADIENTS = {
  sunny: ['#1E5A9F', '#4A90D9', '#7BB8E8'],      // 晴天日间
  rain: ['#1A1A2E', '#16213E', '#0F3460'],         // 雨天日间
  snow: ['#2C3E50', '#34495E', '#5D6D7E'],         // 雪天日间
  // ... 夜间版本颜色更深
};

5.4 桌面小组件

通过Form Extension Ability实现三种尺寸的天气卡片:

  • 2x2:仅显示当前温度和天气图标
  • 2x4:温度 + 天气描述 + 高低温
  • 4x4:完整天气信息 + 逐小时预报

小组件支持定时刷新和点击跳转App对应页面。

5.5 自定义图表组件

没有使用第三方图表库,全部基于ArkTS自定义绘制:

组件实现方式特点
TempCurveChartCanvas路径绘制平滑贝塞尔曲线,渐变填充,高低温双线
AqiBarChartColumn组件+动画柱状图高度动画,AQI分级配色
WindCompassCanvas旋转罗盘指针随风向旋转,风速显示在中心

六、状态管理方案

项目采用AppStorage全局存储 + Repository单例的状态管理方案:

// 天气数据存入AppStorage,UI通过@StorageProp自动响应
AppStorage.setOrCreate('current_weather_obj', null);

// Index.ets中绑定
@StorageProp('current_weather_obj') weather: MergedWeather | null = null;

// WeatherRepository单例负责数据加载和缓存
export const weatherRepository = new WeatherRepository();

数据流:

用户操作 → WeatherRepository.loadWeather() 
→ WeatherMergeService.fetchAndMerge() 
→ 双API并发请求 → FieldMerger合并 
→ AppStorage更新 → @StorageProp自动触发UI刷新

七、网络请求封装

HttpClient统一封装了超时、重试、gzip、错误处理:

// 支持连接超时/传输超时分离配置
timeout: { connect: 5000, transfer: 10000 }
// 指数退避重试
retryBackoffMs: 500
// 统一错误分类:HttpError(网络层)/ BusinessError(业务层)

八、开发踩坑记录

8.1 双源合并的adcode问题

高德天气API需要adcode(城市编码),而定位只能拿到经纬度。解决方案:首次定位时调用高德逆地理编码接口获取adcode并缓存,后续直接使用。

8.2 AI流式输出的解析

DeepSeek和通义千问的SSE格式略有差异,需要统一的StreamParser处理data: 前缀和[DONE]结束标记。

8.3 Canvas粒子性能优化

粒子数量过多会导致帧率下降,解决方案:根据设备性能动态调整粒子数量,低端设备减少粒子数并降低绘制频率。

8.4 后台预警轮询

HarmonyOS NEXT对后台任务限制严格,需要使用backgroundTaskManager申请短时任务,配合系统闹钟实现定期轮询。

8.5 桌面卡片刷新限制

Form卡片有刷新频率限制(默认30分钟一次),需要在用户点击时主动触发刷新,避免数据过旧。

九、总结

WinkWeatherPulse项目覆盖了HarmonyOS NEXT开发的核心知识点:ArkTS声明式UI、AppStorage状态管理、Canvas自定义绘制、网络请求封装、Form小组件、后台任务、AI API集成等。

双源数据合并和AI天气助手是项目的两大特色,前者提升了数据可靠性,后者让天气App从"工具"升级为"助手"。动态背景和自定义图表则提升了整体视觉体验。

如果你也在学习HarmonyOS NEXT开发,这个项目可以作为一个不错的实战参考。后续计划加入更多功能:天气雷达图动画、语音播报、智能出行建议等。

参考资料

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐