news 2026/4/18 14:02:15

HarmonyOS 6学习:应用优雅退出与后台长时任务管理——告别“幽灵音乐”,打造纯净退出体验

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HarmonyOS 6学习:应用优雅退出与后台长时任务管理——告别“幽灵音乐”,打造纯净退出体验

引言:当应用变成“幽灵播放器”

想象这样一个场景:用户在地铁上打开你的音乐应用,享受一段美妙的旅程。到站后,他习惯性地侧滑两次退出应用,收起手机准备下车。然而,耳机里传来的音乐并未停止——你的应用像“幽灵”一样在后台继续播放着音乐。用户不得不重新打开手机,找到你的应用,手动停止播放。这种糟糕的体验,很可能让你的应用被贴上“流氓应用”的标签。

这并非你的应用有意为之,而是HarmonyOS应用生命周期管理中一个常见但容易被忽视的技术细节:应用进入后台状态而非完全终止。当应用拥有后台长时任务权限时,即使用户通过侧滑退出,应用仍然可以在后台运行,继续执行播放音乐、导航等任务。

本文将带你深入HarmonyOS 6的应用生命周期管理机制,从问题根源到实战解决方案,手把手教你如何让应用成为“礼貌的客人”,该离开时就彻底离开,不留任何“幽灵任务”。

一、问题重现:“幽灵音乐”的案发现场

1.1 典型问题现象

用户在使用音乐、听书、导航等应用时,通过连续两次侧滑退出应用(系统提示“再按一次退出”),应用界面确实退出了,但后台声音仍在继续播放。具体表现为:

  • 音乐应用:退出后音乐继续播放,用户需要手动打开应用停止

  • 听书应用:退出后朗读继续,消耗流量和电量

  • 导航应用:退出后导航语音仍在播报,干扰用户

  • 录音应用:退出后录音仍在继续,侵犯用户隐私

1.2 问题影响范围

应用类型

后台任务类型

用户感知

严重程度

音乐/音频类

音频播放

音乐/播客继续播放

导航类

语音播报、位置更新

导航语音干扰

极高

录音类

录音录制

隐私泄露风险

极高

下载类

文件下载

流量消耗

健身类

运动数据记录

数据不准确

二、技术原理:为什么应用“死而不僵”?

2.1 核心概念解析

要理解“幽灵音乐”现象,首先需要掌握三个关键概念:

1. 应用生命周期状态

  • 前台状态 (Foreground):应用界面可见,用户正在交互

  • 后台状态 (Background):应用界面不可见,但进程仍在运行

  • 终止状态 (Terminated):应用进程完全结束,释放所有资源

2. 后台长时任务 (Background Continuous Task)

HarmonyOS允许某些类型的应用在后台长时间运行,以完成用户可感知的任务:

  • 音频播放(音乐、播客、听书)

  • 位置服务(导航、运动记录)

  • 数据传输(文件下载、同步)

  • 录音录制

3. onBackPress回调机制

  • 属于自定义组件的生命周期回调函数

  • 当用户点击返回按钮或侧滑返回时触发

  • 仅对@Entry装饰的自定义组件生效

  • 返回true表示消费事件,阻止默认行为

2.2 “幽灵音乐”的根本原因

根据官方文档分析,问题的核心在于:应用通过侧滑退出时,默认进入后台状态而非终止状态

错误流程示意

用户第一次侧滑 → 系统提示"再按一次退出" → 用户第二次侧滑 → 应用进入后台 → 长时任务继续运行 → "幽灵音乐"出现

日志证据分析

从问题定位日志可以看出关键信息:

06-30 14:27:36.433 [com.hm.example][entry][100000]: OnBackPressed called 06-30 14:27:36.434 router user onBackPress return true 06-30 14:27:36.770 Ability onBackground1:::true 06-30 14:27:37.559 app is inBackground

关键日志解读:

  • onBackPress return true:应用重写了onBackPress方法并返回true

  • Ability onBackground:Ability进入后台状态

  • app is inBackground:应用确认处于后台状态

这表明应用处理了侧滑事件,但只是进入了后台,进程并未终止。由于应用在启动时申请了后台长时任务权限,音频播放任务得以继续执行。

三、实战解决方案:三步实现优雅退出

3.1 解决方案总览

解决“幽灵音乐”问题的核心思路:在用户确认退出时,主动终止应用进程,而不是让其进入后台

graph TD A[问题: 侧滑退出后音乐继续播放] --> B{根本原因} B --> C[应用进入后台而非终止] C --> D[解决方案] D --> E[步骤1: 监听侧滑事件] D --> F[步骤2: 实现二次确认] D --> G[步骤3: 主动终止进程] E --> H[纯净退出体验] F --> H G --> H

3.2 步骤一:正确实现onBackPress监听

首先需要在入口组件中正确实现onBackPress回调,捕获用户的侧滑退出意图。

// ElegantExitExample.ets - 优雅退出示例组件 import { common } from '@kit.AbilityKit'; import promptAction from '@ohos.promptAction'; @Entry @Component struct ElegantExitExample { // 状态变量 @State currentMessage: string = '欢迎使用音乐播放器'; @State isPlaying: boolean = false; @State playProgress: number = 0; // 退出相关变量 private firstBackTimestamp: number = 0; // 第一次侧滑时间戳 private backPressCount: number = 0; // 侧滑次数计数 // 获取UIAbility上下文 private context: common.UIAbilityContext = this.getUIContext().getHostContext() as common.UIAbilityContext; // 模拟音乐播放器 private musicPlayer = { play: () => { this.isPlaying = true; this.currentMessage = '正在播放: 月光奏鸣曲'; console.info('音乐开始播放'); }, pause: () => { this.isPlaying = false; this.currentMessage = '音乐已暂停'; console.info('音乐暂停'); }, stop: () => { this.isPlaying = false; this.playProgress = 0; this.currentMessage = '音乐已停止'; console.info('音乐停止'); } }; aboutToAppear() { // 模拟应用启动时申请后台长时任务 this.requestBackgroundTask(); } // 模拟申请后台长时任务 requestBackgroundTask() { console.info('申请音频播放后台长时任务权限'); // 实际开发中需要调用backgroundTaskManager相关API // 这里仅作演示 } build() { Column({ space: 20 }) { // 应用标题 Text('优雅退出演示应用') .fontSize(24) .fontWeight(FontWeight.Bold) .fontColor('#1890FF') .margin({ top: 40 }) // 音乐播放状态 Text(this.currentMessage) .fontSize(18) .fontColor('#333333') .margin({ top: 20 }) // 播放进度条 Progress({ value: this.playProgress, total: 100 }) .width('80%') .height(10) .color('#1890FF') .backgroundColor('#E8E8E8') .margin({ top: 20 }) // 控制按钮 Row({ space: 30 }) { Button(this.isPlaying ? '暂停' : '播放') .width(100) .backgroundColor(this.isPlaying ? '#FF4D4F' : '#52C41A') .onClick(() => { if (this.isPlaying) { this.musicPlayer.pause(); } else { this.musicPlayer.play(); this.simulatePlayProgress(); } }) Button('停止') .width(100) .backgroundColor('#FAAD14') .onClick(() => { this.musicPlayer.stop(); }) } .margin({ top: 30 }) // 操作提示 Text('提示: 连续侧滑两次退出应用') .fontSize(14) .fontColor('#888888') .margin({ top: 40 }) Text(`侧滑计数: ${this.backPressCount}`) .fontSize(12) .fontColor('#666666') .margin({ top: 10 }) } .width('100%') .height('100%') .padding(20) .backgroundColor('#F5F5F5') } // 模拟播放进度 simulatePlayProgress() { if (this.isPlaying && this.playProgress < 100) { setTimeout(() => { this.playProgress += 1; this.simulatePlayProgress(); }, 100); } } // 关键: onBackPress回调实现 onBackPress(): boolean | void { const now = Date.now(); this.backPressCount += 1; // 第一次侧滑: 显示提示 if (now - this.firstBackTimestamp > 1000) { this.firstBackTimestamp = now; promptAction.showToast({ message: '再侧滑一次退出应用', duration: 1000, bottom: 200 }); console.info('第一次侧滑,显示退出提示'); return true; // 消费事件,阻止默认退出 } // 第二次侧滑(1秒内): 执行退出 if (now - this.firstBackTimestamp <= 1000) { console.info('第二次侧滑,执行应用终止'); // 停止所有后台任务 this.cleanupBeforeExit(); // 主动终止应用进程 this.context.terminateSelf() .then(() => { console.info('应用终止成功'); }) .catch((err: BusinessError) => { console.error(`应用终止失败: ${err.code}, ${err.message}`); }); return true; // 消费事件 } return true; } // 退出前的清理工作 cleanupBeforeExit() { console.info('开始清理工作...'); // 1. 停止音乐播放 this.musicPlayer.stop(); // 2. 释放音频资源 this.releaseAudioResources(); // 3. 停止后台长时任务 this.stopBackgroundTask(); // 4. 保存应用状态 this.saveApplicationState(); console.info('清理工作完成'); } // 释放音频资源 releaseAudioResources() { // 实际开发中需要释放AudioRenderer等资源 console.info('音频资源已释放'); } // 停止后台长时任务 stopBackgroundTask() { // 实际开发中需要调用backgroundTaskManager.stopBackgroundRunning console.info('后台长时任务已停止'); } // 保存应用状态 saveApplicationState() { // 保存播放进度、用户设置等 const appState = { lastPlayProgress: this.playProgress, lastPlayTime: Date.now(), isPlaying: this.isPlaying }; // 使用PersistentStorage或Preferences保存 console.info('应用状态已保存:', appState); } }

3.3 步骤二:完整的生命周期管理

除了处理侧滑退出,还需要全面管理应用的生命周期,确保在各种场景下都能正确释放资源。

// LifecycleManager.ets - 完整的生命周期管理器 import { common, AbilityConstant } from '@kit.AbilityKit'; import { BusinessError } from '@kit.BasicServicesKit'; import backgroundTaskManager from '@kit.BackgroundTaskManager'; export class LifecycleManager { private static instance: LifecycleManager; private context: common.UIAbilityContext; private isBackgroundTaskRunning: boolean = false; private backgroundTaskId: number = -1; // 单例模式 private constructor(context: common.UIAbilityContext) { this.context = context; } static getInstance(context: common.UIAbilityContext): LifecycleManager { if (!LifecycleManager.instance) { LifecycleManager.instance = new LifecycleManager(context); } return LifecycleManager.instance; } // 初始化生命周期监听 initLifecycleListeners() { // 监听Ability生命周期 this.context.on('abilityLifecycle', (lifecycleState: AbilityConstant.LifecycleState) => { this.handleAbilityLifecycle(lifecycleState); }); // 监听窗口生命周期 this.context.on('windowStageLifecycle', (stageLifecycleState: AbilityConstant.WindowStageLifecycleState) => { this.handleWindowStageLifecycle(stageLifecycleState); }); console.info('生命周期监听器已初始化'); } // 处理Ability生命周期 private handleAbilityLifecycle(state: AbilityConstant.LifecycleState) { switch (state) { case AbilityConstant.LifecycleState.CREATE: console.info('Ability创建'); break; case AbilityConstant.LifecycleState.FOREGROUND: console.info('Ability进入前台'); this.onForeground(); break; case AbilityConstant.LifecycleState.BACKGROUND: console.info('Ability进入后台'); this.onBackground(); break; case AbilityConstant.LifecycleState.DESTROY: console.info('Ability销毁'); this.onDestroy(); break; } } // 处理窗口生命周期 private handleWindowStageLifecycle(state: AbilityConstant.WindowStageLifecycleState) { switch (state) { case AbilityConstant.WindowStageLifecycleState.SHOWN: console.info('窗口显示'); break; case AbilityConstant.WindowStageLifecycleState.ACTIVE: console.info('窗口激活'); break; case AbilityConstant.WindowStageLifecycleState.INACTIVE: console.info('窗口失活'); break; case AbilityConstant.WindowStageLifecycleState.HIDDEN: console.info('窗口隐藏'); break; } } // 进入前台时的处理 private onForeground() { console.info('应用进入前台,恢复必要服务'); // 恢复音频播放(如果需要) // this.resumeAudioPlayback(); // 更新UI状态 // this.updateUIState(); } // 进入后台时的处理 private onBackground() { console.info('应用进入后台,暂停非必要任务'); // 暂停音频播放 // this.pauseAudioPlayback(); // 减少资源消耗 // this.reduceResourceUsage(); // 检查是否需要继续后台任务 if (!this.shouldKeepBackgroundTask()) { this.stopBackgroundTaskIfNeeded(); } } // 销毁时的清理 private onDestroy() { console.info('应用销毁,释放所有资源'); // 停止所有后台任务 this.stopAllBackgroundTasks(); // 释放所有资源 this.releaseAllResources(); // 保存最终状态 this.saveFinalState(); } // 启动后台长时任务 async startBackgroundTask(taskInfo: backgroundTaskManager.BackgroundMode): Promise<boolean> { try { const want = { bundleName: this.context.abilityInfo.bundleName, abilityName: this.context.abilityInfo.name }; const result = await backgroundTaskManager.startBackgroundRunning(this.context, want, taskInfo); if (result === backgroundTaskManager.BackgroundMode.CONTINUOUS) { this.isBackgroundTaskRunning = true; console.info('后台长时任务启动成功'); return true; } console.warn('后台长时任务启动失败'); return false; } catch (err) { const error = err as BusinessError; console.error(`启动后台任务失败: ${error.code}, ${error.message}`); return false; } } // 停止后台长时任务 async stopBackgroundTaskIfNeeded(): Promise<boolean> { if (!this.isBackgroundTaskRunning) { return true; } try { await backgroundTaskManager.stopBackgroundRunning(this.context); this.isBackgroundTaskRunning = false; console.info('后台长时任务已停止'); return true; } catch (err) { const error = err as BusinessError; console.error(`停止后台任务失败: ${error.code}, ${error.message}`); return false; } } // 判断是否需要保持后台任务 private shouldKeepBackgroundTask(): boolean { // 根据应用逻辑判断 // 例如:音乐正在播放、导航正在进行、文件正在下载等 // 这里简化处理,实际应根据业务逻辑判断 return false; } // 停止所有后台任务 private stopAllBackgroundTasks() { if (this.isBackgroundTaskRunning) { this.stopBackgroundTaskIfNeeded(); } // 停止其他可能的任务 // this.stopAudioPlayback(); // this.stopLocationUpdates(); // this.stopDownloadTasks(); } // 释放所有资源 private releaseAllResources() { console.info('释放所有资源'); // 释放音频资源 // this.releaseAudioResources(); // 释放网络连接 // this.releaseNetworkConnections(); // 释放文件句柄 // this.releaseFileHandles(); } // 保存最终状态 private saveFinalState() { console.info('保存应用最终状态'); // 保存用户偏好 // this.saveUserPreferences(); // 保存播放进度 // this.savePlaybackProgress(); // 保存未完成的任务 // this.savePendingTasks(); } // 优雅终止应用 async terminateGracefully(): Promise<void> { console.info('开始优雅终止应用'); // 1. 停止所有后台任务 await this.stopAllBackgroundTasks(); // 2. 保存应用状态 this.saveFinalState(); // 3. 释放资源 this.releaseAllResources(); // 4. 终止应用 try { await this.context.terminateSelf(); console.info('应用优雅终止完成'); } catch (err) { const error = err as BusinessError; console.error(`终止应用失败: ${error.code}, ${error.message}`); throw err; } } }

3.4 步骤三:集成到实际应用中

将优雅退出机制集成到实际的应用架构中。

// MainAbility.ts - 主Ability集成生命周期管理 import { UIAbility, AbilityConstant, Want } from '@kit.AbilityKit'; import { window } from '@kit.ArkUI'; import { LifecycleManager } from './LifecycleManager'; export default class MainAbility extends UIAbility { private lifecycleManager: LifecycleManager | undefined; // Ability创建时调用 onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { console.info('MainAbility onCreate'); // 初始化生命周期管理器 this.lifecycleManager = LifecycleManager.getInstance(this.context); this.lifecycleManager.initLifecycleListeners(); } // 窗口创建时调用 onWindowStageCreate(windowStage: window.WindowStage): void { console.info('MainAbility onWindowStageCreate'); // 设置主页面 windowStage.loadContent('pages/Index', (err) => { if (err) { console.error(`加载页面失败: ${err.code}, ${err.message}`); return; } console.info('页面加载成功'); }); } // Ability进入前台时调用 onForeground(): void { console.info('MainAbility onForeground'); // 恢复应用状态 // this.restoreApplicationState(); } // Ability进入后台时调用 onBackground(): void { console.info('MainAbility onBackground'); // 检查是否需要保持后台任务 const shouldKeepTask = this.checkBackgroundTaskNeeded(); if (!shouldKeepTask && this.lifecycleManager) { // 如果没有需要保持的后台任务,准备终止 this.prepareForTermination(); } } // Ability销毁时调用 onDestroy(): void { console.info('MainAbility onDestroy'); // 执行最终清理 if (this.lifecycleManager) { // 生命周期管理器会处理清理工作 } } // 检查是否需要后台任务 private checkBackgroundTaskNeeded(): boolean { // 根据业务逻辑判断 // 例如:检查是否有正在播放的音乐、正在进行的导航等 // 这里简化处理,实际应从状态管理器中获取 return false; } // 准备终止应用 private prepareForTermination(): void { console.info('准备终止应用'); // 可以在这里添加一些延迟,给用户返回的机会 setTimeout(() => { if (this.lifecycleManager) { this.lifecycleManager.terminateGracefully() .catch((err: BusinessError) => { console.error(`优雅终止失败: ${err.code}, ${err.message}`); }); } }, 5000); // 5秒后如果没有回到前台,则终止 } // 恢复应用状态 private restoreApplicationState(): void { console.info('恢复应用状态'); // 从持久化存储中恢复状态 // const savedState = this.loadSavedState(); // this.applySavedState(savedState); } }

四、高级技巧与最佳实践

4.1 处理特殊场景

场景一:用户快速切换应用

用户可能只是暂时切换到其他应用,稍后会返回。此时不应立即终止应用。

解决方案

// 在onBackground中添加延迟判断 private backgroundTimer: number | undefined; onBackground() { console.info('应用进入后台'); // 清除之前的定时器 if (this.backgroundTimer) { clearTimeout(this.backgroundTimer); } // 设置5秒延迟,如果用户5秒内没有返回前台,则准备终止 this.backgroundTimer = setTimeout(() => { if (!this.isAppReturningForeground) { this.prepareForTermination(); } }, 5000); } onForeground() { console.info('应用返回前台'); this.isAppReturningForeground = true; // 清除终止定时器 if (this.backgroundTimer) { clearTimeout(this.backgroundTimer); this.backgroundTimer = undefined; } }
场景二:后台任务需要继续执行

某些应用确实需要在后台继续执行任务,如音乐播放、导航、录音等。

解决方案

// 根据任务类型决定是否保持后台运行 private shouldKeepBackgroundTask(): boolean { // 检查当前任务状态 const taskManager = TaskManager.getInstance(); // 音乐正在播放 if (taskManager.isMusicPlaying) { return true; } // 导航正在进行 if (taskManager.isNavigationActive) { return true; } // 录音正在进行 if (taskManager.isRecording) { return true; } // 文件正在下载 if (taskManager.hasActiveDownloads) { return true; } return false; }

4.2 用户体验优化

优化一:智能退出确认

根据应用使用场景,智能判断是否需要二次确认。

// 智能退出确认逻辑 private shouldShowExitConfirmation(): boolean { // 如果有未保存的工作 if (this.hasUnsavedWork) { return true; } // 如果正在执行重要任务 if (this.isCriticalTaskRunning) { return true; } // 如果用户设置了需要确认 if (this.userPreferences.alwaysConfirmExit) { return true; } // 默认不需要确认 return false; } onBackPress(): boolean | void { if (this.shouldShowExitConfirmation()) { this.showExitConfirmationDialog(); return true; } // 直接退出 return this.performGracefulExit(); }
优化二:状态保存与恢复

确保用户再次打开应用时,能恢复到之前的状态。

// 状态保存与恢复 export class StateManager { private static readonly STATE_KEY = 'app_state'; // 保存状态 static saveState(state: AppState): void { try { const stateStr = JSON.stringify(state); // 使用Preferences保存 // preferences.putSync(this.STATE_KEY, stateStr); console.info('应用状态已保存'); } catch (err) { console.error('保存状态失败:', err); } } // 恢复状态 static restoreState(): AppState | null { try { // 从Preferences读取 // const stateStr = preferences.getSync(this.STATE_KEY, ''); // return JSON.parse(stateStr); return null; } catch (err) { console.error('恢复状态失败:', err); return null; } } // 清除状态 static clearState(): void { try { // preferences.deleteSync(this.STATE_KEY); console.info('应用状态已清除'); } catch (err) { console.error('清除状态失败:', err); } } }

4.3 测试与验证

测试用例设计

测试场景

预期结果

测试方法

正常侧滑退出

应用完全终止,无后台任务

侧滑两次,检查进程

快速切换应用

应用在后台保持,可快速恢复

切换到其他应用,5秒内返回

后台任务运行中退出

任务停止,应用终止

播放音乐时侧滑退出

异常退出处理

资源正确释放,无内存泄漏

强制停止应用

日志监控
// 添加详细的日志记录 class ExitLogger { static logExitAttempt(timestamp: number, reason: string): void { console.info(`[退出尝试] 时间: ${new Date(timestamp).toISOString()}, 原因: ${reason}`); } static logExitSuccess(timestamp: number): void { console.info(`[退出成功] 时间: ${new Date(timestamp).toISOString()}`); } static logExitFailure(timestamp: number, error: BusinessError): void { console.error(`[退出失败] 时间: ${new Date(timestamp).toISOString()}, 错误: ${error.code}, ${error.message}`); } static logResourceRelease(resourceType: string, success: boolean): void { const status = success ? '成功' : '失败'; console.info(`[资源释放] 类型: ${resourceType}, 状态: ${status}`); } }

五、总结与展望

通过本文的学习,你已经掌握了解决HarmonyOS应用“幽灵音乐”问题的完整方案。从问题定位到实战解决,关键在于理解应用生命周期和正确处理退出逻辑。

核心要点回顾

  1. 问题根源:应用侧滑退出时默认进入后台而非终止,后台长时任务继续运行

  2. 解决方案:在onBackPress中主动调用terminateSelf()终止应用

  3. 关键步骤:监听侧滑事件 → 实现二次确认 → 清理资源 → 终止进程

  4. 最佳实践:智能判断退出时机、妥善保存应用状态、全面释放资源

未来展望

随着HarmonyOS生态的不断发展,应用生命周期管理将更加智能化。未来可能会有:

  • 基于AI的智能退出预测

  • 更精细的后台任务管理

  • 跨设备协同的生命周期管理

  • 用户习惯学习,自动优化退出行为

从今天开始,让你的应用告别“幽灵音乐”,为用户提供纯净、可预测的退出体验。当用户能够放心地退出你的应用,而不必担心后台任务继续消耗资源时,他们会用更高的满意度和更长的使用时间来回报你的用心。

记住:优秀的应用不仅要知道如何运行,更要知道如何优雅地结束。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 14:01:02

巧用Python模拟POP3服务,揭秘Foxmail本地加密密码获取新思路

1. 为什么需要模拟POP3服务获取Foxmail密码 相信很多使用Foxmail的朋友都遇到过这样的尴尬&#xff1a;邮箱密码明明保存在本地&#xff0c;但时间一长就忘记了。这时候如果去重置密码&#xff0c;不仅要走繁琐的流程&#xff0c;还可能影响正常工作。更麻烦的是&#xff0c;现…

作者头像 李华
网站建设 2026/4/18 13:58:39

21天麻将AI训练指南:如何用Akagi从菜鸟变高手

21天麻将AI训练指南&#xff1a;如何用Akagi从菜鸟变高手 【免费下载链接】Akagi 支持雀魂、天鳳、麻雀一番街、天月麻將&#xff0c;能夠使用自定義的AI模型實時分析對局並給出建議&#xff0c;內建Mortal AI作為示例。 Supports Majsoul, Tenhou, Riichi City, Amatsuki, wit…

作者头像 李华
网站建设 2026/4/18 13:58:20

5分钟极速指南:用Winhance全面掌控Windows系统优化与个性化配置

5分钟极速指南&#xff1a;用Winhance全面掌控Windows系统优化与个性化配置 【免费下载链接】Winhance-zh_CN A Chinese version of Winhance. C# application designed to optimize and customize your Windows experience. 项目地址: https://gitcode.com/gh_mirrors/wi/Wi…

作者头像 李华
网站建设 2026/4/18 13:57:48

抖音去水印批量下载工具:如何一键保存用户主页全部作品

抖音去水印批量下载工具&#xff1a;如何一键保存用户主页全部作品 【免费下载链接】TikTokDownload 抖音去水印批量下载用户主页作品、喜欢、收藏、图文、音频 项目地址: https://gitcode.com/gh_mirrors/ti/TikTokDownload 还在为抖音上精彩的内容无法完整保存而烦恼吗…

作者头像 李华
网站建设 2026/4/18 13:57:23

Outfit字体:一站式革命性品牌视觉解决方案

Outfit字体&#xff1a;一站式革命性品牌视觉解决方案 【免费下载链接】Outfit-Fonts The most on-brand typeface 项目地址: https://gitcode.com/gh_mirrors/ou/Outfit-Fonts 想象一下&#xff0c;你正在为一个新品牌设计视觉系统&#xff0c;从logo到网站&#xff0c…

作者头像 李华