【星光不负 码向未来】3 周打造「极简天气」的踩坑与成长

摘要

作为一名有三年开发经验的程序员,我对「元服务」的最初认知停留在「轻量级应用」的概念上。

前言

作为一名有三年开发经验的程序员,我对「元服务」的最初认知停留在「轻量级应用」的概念上。2024 年 HarmonyOS 创新赛启动时,我决定挑战自己——从零开始开发一个 极简天气元服务,不求功能复杂,只求把鸿蒙元服务的核心特性「吃透」。这三周的开发历程,从对着文档发懵到最终完成作品提交,每个环节都藏着对鸿蒙生态的新理解。

一、选题:为什么是「极简天气」?

选择天气场景,是因为它天然契合元服务的「轻量化、即时性」特点:用户不需要打开 App,从服务中心的卡片就能快速查看气温、天气状况,符合「服务直达」的鸿蒙理念。同时,天气数据的获取、展示逻辑相对简单,适合作为元服务开发的入门实践。

我给这个元服务定了三个核心目标:

  • 桌面卡片实时显示当前城市气温、天气;

  • 支持点击卡片快速切换城市;

  • 数据本地缓存,无网络时也能查看历史天气。

二、技术攻坚:和元服务「死磕」的三个关键节点

1. 卡片适配:从「模拟器好看」到「多设备能用」

元服务的核心载体是桌面卡片,但我最初的实现完全忽略了「多设备适配」。写了个固定尺寸的卡片,在手机模拟器上看着挺精致,装到导师的华为平板上却发现:文字被挤成一团,天气图标显示不全。

// 最初的错误尝试:固定像素布局 @Entry@Componentstruct WeatherCard {
  build() {
    Column() {
      Text("北京 25℃ 晴")
        .fontSize(16)
      Image("sunny.png")
        .width(40)
        .height(40)
    }
    .width(200) // 固定宽度,平板上直接「崩了」    .height(100)
  }
}

翻阅 HarmonyOS NEXT 官方文档的「元服务适配指南」后,我改用弹性布局(Flex)+ 动态资源,去掉所有固定尺寸,让卡片「自适应」不同设备的桌面空间:

// 优化后:弹性布局+动态资源,多设备友好 @Entry@Componentstruct WeatherCard {
  @State city: string = "北京"  @State temp: number = 25  @State condition: string = "晴"  build() {
    Column({ space: 5 }) {
      Text(this.city)
        .fontSize($r('app.float.card_title_size'))
        .fontWeight(FontWeight.Bold)
      Row({ space: 8 }) {
        Text(`${this.temp}℃`)
          .fontSize($r('app.float.card_content_size'))
        Image(`${this.condition}.png`)
          .width('20vp')
          .height('20vp')
      }
    }
    .width('100%') // 占满卡片可分配宽度    .height('100%')
    .padding($r('app.float.card_padding'))
  }
}

同时在 resources/base/element/resource.json 中配置动态资源:

{
  "float": {
    "card_title_size": { "value": "14vp" },
    "card_content_size": { "value": "12vp" },
    "card_padding": { "value": "8vp" }
  }
}

这个调整花了我整整两天。期间我还加入了华为开发者论坛的「元服务交流群」,请教群友后才明白:vp 单位是鸿蒙适配的关键,它能自动根据设备屏幕密度调整尺寸。优化后,卡片在手机、平板上终于「各得其所」——手机端紧凑显示核心数据,平板端扩展展示更多信息(如空气质量)。

2. 数据管理:从「网络依赖」到「离线可用」

天气数据需要实时获取,但完全依赖网络会导致「无网时卡片空白」。我决定用 Preferences 本地缓存,实现「有网更新、无网读缓存」的策略。

最初的代码只处理了网络请求,没有缓存逻辑:

// 最初的错误:无缓存,无网时数据丢失 async getWeather(city: string) {
  const response = await fetch(`https://api.weather.com/${city}`);  const data = await response.json();
  this.temp = data.temp;
  this.condition = data.condition;
}

优化后,我加入了缓存读取和写入逻辑:

// 优化后:网络+缓存双保险 import preferences from '@ohos.data.preferences';let prefs: preferences.Preferences | null = null;@Entry@Componentstruct WeatherCard {
  @State city: string = "北京"  @State temp: number = 0  @State condition: string = "未知"  async aboutToAppear() {
    // 初始化 Preferences    prefs = await preferences.getPreferences(this.context, 'weatherCache');
    // 先读缓存    const cached = await prefs.get(`${this.city}_weather`, '{}');
    const cacheData = JSON.parse(cached);
    if (cacheData.temp) {
      this.temp = cacheData.temp;
      this.condition = cacheData.condition;
    }
    // 再请求网络更新    this.getWeather();
  }
  async getWeather() {
    try {
      const response = await fetch(`https://api.weather.com/${this.city}`);
      const data = await response.json();
      this.temp = data.temp;
      this.condition = data.condition;
      // 写入缓存      await prefs.put(`${this.city}_weather`, JSON.stringify({
        temp: data.temp,
        condition: data.condition
      }));
      await prefs.flush();
    } catch (err) {
      console.error('网络请求失败,使用缓存数据', err);
    }
  }
  // 点击切换城市的逻辑...}

这个改动让元服务在无网络时也能显示历史天气,大大提升了实用性。测试时我特意断网打开卡片,看到缓存的天气数据正常显示时,才真正体会到鸿蒙「本地优先」的设计理念。

3. 用户交互:从「单一展示」到「可操作」

元服务不能只是「静态卡片」,需要支持基础交互。我给卡片添加了「点击切换城市」的功能,但最初的实现存在「点击无反馈」的问题。

// 优化前:点击无反馈,用户不知道操作是否生效 Text(this.city)
  .onClick(() => {
    this.city = this.city === "北京" ? "上海" : "北京";
    this.getWeather();
  })

优化后,我加入了微动效和加载状态,让用户明确感知操作过程:

// 优化后:点击有反馈,体验更友好 @State isLoading: boolean = false;
Text(this.city)
  .fontSize($r('app.float.card_title_size'))
  .fontWeight(FontWeight.Bold)
  .onClick(async () => {
    this.isLoading = true; // 显示加载状态    this.city = this.city === "北京" ? "上海" : "北京";
    await this.getWeather();
    this.isLoading = false; // 加载完成  })
  .loading(this.isLoading) // 鸿蒙自带的加载动效

同时,我还在服务中心配置了「快捷切换城市」的入口,用户长按卡片就能选择常用城市,进一步强化了「服务直达」的体验。

三、开发感悟:元服务的「轻」与「重」

回顾这三周的开发,我对鸿蒙元服务的理解从「技术概念」变成了「实战认知」:

  • 「轻」在形态,「重」在体验:元服务的代码量可以很少,但对「多设备适配」「用户交互细节」的要求一点都不低。一个卡片的适配问题,可能需要反复调试才能解决。

  • 文档是最好的老师:鸿蒙官方文档的「元服务开发指南」和「API 参考」是我解决问题的核心依据。遇到卡点时,先查文档,再去论坛提问,效率远高于自己瞎琢磨。

  • 小场景也能体现价值:天气元服务功能简单,但它完美契合了「快速查看、即时操作」的元服务定位。这让我明白,开发元服务不必追求「大而全」,把一个小场景做深做透,就是成功。

结语

虽然最终的「极简天气」元服务没有在赛事中获得亮眼成绩,但这段开发经历让我真正「入门」了鸿蒙生态。它不再是我眼中「遥远的新技术」,而是一套「能落地、能解决实际问题」的开发工具。如果再开发元服务,我会更早关注「用户操作路径」和「多设备一致性体验」,让元服务的「轻」发挥出最大价值。对于鸿蒙开发新手,我建议从类似的小场景入手,先把基础特性用扎实,再逐步探索更复杂的能力——毕竟,每个大神都是从「踩坑」开始成长的。

来源:互联网

最新文章

极客公园

用极客视角,追踪你不可错过的科技圈.

极客之选

新鲜、有趣的硬件产品,第一时间为你呈现。

张鹏科技商业观察

聊科技,谈商业。