网罗开发 (小红书、快手、视频号同名)

  大家好,我是 展菲,目前在上市企业从事人工智能项目研发管理工作,平时热衷于分享各种编程领域的软硬技能知识以及前沿技术,包括iOS、前端、Harmony OS、Java、Python等方向。在移动端开发、鸿蒙开发、物联网、嵌入式、云原生、开源等领域有深厚造诣。

图书作者:《ESP32-C3 物联网工程开发实战》
图书作者:《SwiftUI 入门,进阶与实战》
超级个体:COC上海社区主理人
特约讲师:大学讲师,谷歌亚马逊分享嘉宾
科技博主:华为HDE/HDG

我的博客内容涵盖广泛,主要分享技术教程、Bug解决方案、开发工具使用、前沿科技资讯、产品评测与使用体验。我特别关注云服务产品评测、AI 产品对比、开发板性能测试以及技术报告,同时也会提供产品优缺点分析、横向对比,并分享技术沙龙与行业大会的参会体验。我的目标是为读者提供有深度、有实用价值的技术洞察与分析。

展菲:您的前沿技术领航员
👋 大家好,我是展菲!
📱 全网搜索“展菲”,即可纵览我在各大平台的知识足迹。
📣 公众号“Swift社区”,每周定时推送干货满满的技术长文,从新兴框架的剖析到运维实战的复盘,助您技术进阶之路畅通无阻。
💬 微信端添加好友“fzhanfei”,与我直接交流,不管是项目瓶颈的求助,还是行业趋势的探讨,随时畅所欲言。
📅 最新动态:2025 年 3 月 17 日
快来加入技术社区,一起挖掘技术的无限潜能,携手迈向数字化新征程!


前言

在鸿蒙应用里,只要一提到“后台”,大多数人都会下意识紧张:

  • 会不会被系统杀?
  • 为啥刚切到后台,服务就没了?
  • 音乐、导航这种 App,凭什么能一直活着?

如果你正在用 ServiceExtensionAbility,那你绕不开两个概念:

短时任务(Transient Task)
长时任务(Continuous Task)

这两个不是“名字不同”,而是系统调度策略完全不一样。选错了,代码写得再优雅也没用。

这篇文章我就从工程实践的角度,把它们彻底拆清楚。

为什么鸿蒙要区分短时任务和长时任务?

先说一个现实问题。

系统最怕什么?
后台无限跑、耗电、占内存、用户完全没感知。

所以鸿蒙从设计上就不鼓励“后台偷偷干活”,而是把后台任务分成两类:

  • 干完就走的
  • 必须长期存在的

系统的态度很明确:

你要是短期工作,我给你窗口
你要是长期工作,你必须“亮身份”

这就是 Transient 和 Continuous 的根本差异。

短时任务适合什么场景?

短时任务只有一个核心特点:

事情做完就可以死

非常适合下面这些场景:

  • 启动后同步一次配置
  • 上报日志、埋点
  • 拉一批数据到本地
  • 简单的后台计算
  • 临时网络请求

这些任务有两个共性:

  1. 生命周期短
  2. 不需要用户持续感知

短时任务的系统行为

  • 系统给你一小段后台执行时间
  • 不需要前台通知
  • 不需要 FOREGROUND_SERVICE 权限
  • 任务结束后,Ability 会被自动回收

不用也不该想着保活

短时任务的 Manifest 配置方式

<serviceExtension
    android:name=".SyncServiceAbility">
    <extension>
        <transientTask />
    </extension>
</serviceExtension>

这段配置只做了一件事:

告诉系统:这是一次性后台任务

没有 restartPolicy,也没有前台服务。

短时任务 Demo:后台同步一次数据

public class SyncServiceAbility extends ServiceExtensionAbility {

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);

        new Thread(() -> {
            try {
                // 模拟网络请求
                Thread.sleep(3000);
                System.out.println("后台数据同步完成");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 主动结束,释放资源
                stopSelf();
            }
        }).start();
    }

    @Override
    public void onStop() {
        super.onStop();
        System.out.println("SyncServiceAbility stopped");
    }
}

代码要点解释

  • 不要在主线程里做耗时操作
  • 任务完成后主动 stopSelf
  • 不要试图“延长生命周期”

短时任务的正确姿势就是:快进快出。

长时任务到底解决什么问题?

长时任务解决的是另一类需求:

用户明确知道你在后台运行,而且就是希望你别停。

典型场景包括:

  • 音乐播放
  • 导航定位
  • 运动轨迹记录
  • 长时间下载
  • 后台语音通话

这些任务如果突然停了,用户体验直接崩盘。

所以系统的要求也更严格:

你可以活,但你要让用户知道你活着

长时任务的关键点是什么?

长时任务有三个“硬性条件”:

  1. Manifest 里明确声明 continuousTask
  2. 必须是前台服务
  3. 必须展示前台通知

缺一个,系统都会找机会干掉你。

长时任务的 Manifest 标准配置

<serviceExtension
    android:name=".MusicServiceAbility">
    <extension>
        <continuousTask>
            <restartPolicy>restartOnUserExit</restartPolicy>
            <foregroundService>
                <notification>音乐正在后台播放</notification>
            </foregroundService>
        </continuousTask>
    </extension>
</serviceExtension>

restartPolicy 该怎么选?

  • restartOnUserExit
    用户退出应用,但业务还在,比如音乐

  • restartOnLowMemory
    系统内存紧张时,尽量拉起,比如导航

注意:这不是“无限复活”,只是提高存活概率。

音乐播放应用是如何正确保活的?

音乐 App 是最标准的 Continuous Task 场景。

必须申请的权限

<uses-permission name="ohos.permission.FOREGROUND_SERVICE"/>
<uses-permission name="ohos.permission.INTERNET"/>

音乐后台服务 Demo

public class MusicServiceAbility extends ServiceExtensionAbility {

    private MediaPlayer mediaPlayer;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);

        // 1. 创建前台通知
        Notification notification = NotificationUtil.createMusicNotification(this);

        // 2. 切到前台服务
        setForeground(true, notification);

        // 3. 初始化播放器
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setDataSource("https://example.com/music.mp3");
        mediaPlayer.prepareAsync();

        mediaPlayer.setOnPreparedListener(MediaPlayer::start);
    }

    @Override
    public void onStop() {
        super.onStop();

        if (mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }
}

为什么一定要 setForeground?

如果你不切前台:

  • 系统认为你在“偷偷后台跑”
  • 内存一紧,第一批被杀
  • 用户毫无心理预期

前台通知本质上是:

我在干活,你看得见

音乐类应用的真实工程细节

在真实项目里,你还会做这些事:

  • 获取 AudioFocus,避免被其他 App 抢占
  • 锁屏控制播放 / 暂停
  • 耳机插拔监听
  • 异常中断自动恢复

但不管你做多少优化,前台服务是底线

导航类应用为什么更“严格”?

导航比音乐更容易被系统盯上:

  • 持续定位
  • GPS 功耗高
  • 数据频繁更新

所以导航类 App 一定要自觉降功耗

导航 Continuous Task 配置

<serviceExtension
    android:name=".NavigationServiceAbility">
    <extension>
        <continuousTask>
            <restartPolicy>restartOnLowMemory</restartPolicy>
            <foregroundService>
                <notification>导航正在后台运行</notification>
            </foregroundService>
        </continuousTask>
    </extension>
</serviceExtension>

导航后台服务 Demo

public class NavigationServiceAbility extends ServiceExtensionAbility {

    private LocationManager locationManager;

    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);

        Notification notification =
                NotificationUtil.createNavigationNotification(this);
        setForeground(true, notification);

        locationManager = LocationManager.getInstance(this);
        locationManager.requestLocationUpdates(
                LocationRequest.create()
                        .setInterval(5000)
                        .setPriority(LocationRequest.PRIORITY_BALANCED_POWER)
        );
    }

    @Override
    public void onStop() {
        super.onStop();

        if (locationManager != null) {
            locationManager.removeUpdates();
        }
    }
}

导航省电的核心思路

  • 非必要不高精度
  • 后台拉长定位间隔
  • 页面不可见时减少刷新
  • 退出导航立刻释放资源

系统杀你,往往不是“恶意”,而是你太耗了。

最容易踩的几个坑

把短时任务当长时用

  • 同步任务写成 continuous
  • 却没前台通知
  • 结果更容易被杀

长时任务忘记释放资源

  • MediaPlayer 没 release
  • 定位监听没 remove
  • 内存越跑越大

以为 restartPolicy 是免死金牌

不是。

它只是让你有机会重启,不是“无限复活”。

总结

  • 只干一件事,干完就走 → Transient Task
  • 用户明确需要你一直在 → Continuous Task

音乐、导航这类应用,不要侥幸,不要抄捷径

  • 前台服务
  • 清晰通知
  • 合理功耗
  • 主动释放

系统不是不让你活,而是希望你活得有分寸

Logo

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

更多推荐