鸿蒙音频通话应用后台保活与音频连续播放方案

鸿蒙音频通话应用后台保活与音频连续播放方案

一、方案概述

在实时音频通话场景中,应用切换至后台、设备息屏后,进程保活、音频采集与音频播放的连续性是核心体验指标。本方案基于鸿蒙系统 ** 长时任务(Background Task)** 机制,实现通话场景下的后台保活与音频全链路正常工作,确保应用在后台 / 息屏状态下,本地音频采集、对端音频播放均不中断、不被系统静音或回收。

二、长时任务配置与实现

2.1 module.json5 权限与后台模式声明

在应用模块配置文件中,声明音频通话所需的后台运行模式与敏感权限,是长时任务生效的基础配置。

module.json5

js 复制代码
"abilities": [
  {
    "name": "EntryAbility",
    "backgroundModes": ["audioPlayback", "audioRecording"]
  }
],
"requestPermissions":[
  {
    "name" : "ohos.permission.MICROPHONE",
    "reason": "$string:reason",
    "usedScene": {
      "abilities": [
        "FormAbility"
      ],
      "when":"always"
    }
  },
  {
    "name": "ohos.permission.KEEP_BACKGROUND_RUNNING",
    "reason": "$string:reason",
    "usedScene": {
      "abilities": [".MainAbility"],
      "when": "always"
    }
  }
]

2.2 动态申请运行时权限

应用运行时主动申请音频、存储、后台运行等敏感权限,获取系统授权,为音频通话与后台保活提供权限基础。

js 复制代码
async requestPermissionsFn() {
  console.log(`requestPermissionsFn entry`);
  try {
    this.atManager.requestPermissionsFromUser(this.appContext, [
      'ohos.permission.MICROPHONE',
      'ohos.permission.READ_MEDIA',
      'ohos.permission.WRITE_MEDIA',
      'ohos.permission.KEEP_BACKGROUND_RUNNING'
    ]).then(() => {
      console.log(`request Permissions success!`);
    })
  } catch (err) {
    console.log(`requestPermissionsFromUser call Failed! error: ${err.code}`);
  }
}

2.3 长时任务管理类实现

封装长时任务的启动与停止逻辑,统一管理后台任务生命周期,适配音频播放 + 采集双场景的长时任务需求。

LongTermTaskModel.ets

js 复制代码
/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import { abilityAccessCtrl, common, PermissionRequestResult, Permissions, wantAgent, WantAgent } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { backgroundTaskManager } from '@kit.BackgroundTasksKit';
import { geoLocationManager } from '@kit.LocationKit';

const TAG: string = '[LongTermTaskModel]';

export class LongTermTaskModel {
  private context: common.UIAbilityContext = AppStorage.get("context") as common.UIAbilityContext;

  // [Start startLongTask_start]
  // Start a long task
  startLongTask(): void {
    let wantAgentInfo: wantAgent.WantAgentInfo = {
      wants: [
        {
          bundleName: this.context.abilityInfo.bundleName,
          abilityName: this.context.abilityInfo.name
        }
      ],
      actionType: wantAgent.OperationType.START_ABILITY,
      requestCode: 0,
      wantAgentFlags: [wantAgent.WantAgentFlags.UPDATE_PRESENT_FLAG]
    };

    try {
      // wantAgent object is obtained by getWantAgent method in WantAgent module
      wantAgent.getWantAgent(wantAgentInfo).then((wantAgentObj: WantAgent) => {
        // backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK | backgroundTaskManager.BackgroundMode.AUDIO_RECORDING
        // backgroundTaskManager.BackgroundMode.VOIP
        backgroundTaskManager.startBackgroundRunning(this.context, backgroundTaskManager.BackgroundMode.AUDIO_PLAYBACK | backgroundTaskManager.BackgroundMode.AUDIO_RECORDING,
          wantAgentObj)
          .then(() => {
            hilog.info(0x0000, TAG, `Operation startBackgroundRunning succeeded`);
          })
          .catch((error: BusinessError) => {
            hilog.error(0x0000, TAG,
              `Operation startBackgroundRunning failed. code is ${error.code} message is ${error.message}`);
          });
      });
    } catch (error) {
      hilog.error(0x0000, TAG, `Operation getWantAgent failed. error is ${JSON.stringify(error)} `);
    }
  }
  // [End startLongTask_start]

  // [Start stopLongTask_start]
  // Stop a long task
  stopLongTask(): void {
    backgroundTaskManager.stopBackgroundRunning(this.context).then(() => {
      hilog.info(0x0000, TAG, `Operation stopBackgroundRunning succeeded`);
    }).catch((error: BusinessError) => {
      hilog.error(0x0000, TAG, `Operation stopBackgroundRunning failed. error is ${JSON.stringify(error)} `);
    });
  }
  // [End stopLongTask_start]
}

在EntryAbility.ets文件onCreate函数中增加context的设置

备注:设置后再LongTermTaskModel.ets调用

bash 复制代码
export default class EntryAbility extends UIAbility {
  onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
    AppStorage.setOrCreate('context', this.context);
  }
 }

2.4 长时任务调用方式

在业务组件中初始化长时任务实例,在通话开始时启动任务,保证后台生命周期。

js 复制代码
import { LongTermTaskModel } from './LongTermTaskModel';

// 声明组件级任务实例
private taskModel: LongTermTaskModel = new LongTermTaskModel();

// 业务逻辑中调用启动
this.taskModel.startLongTask();

三、后台音频持续播放解决方案

3.1 问题原理

鸿蒙系统对后台应用的音频资源存在默认管控策略:应用切至后台后,系统会优先释放音频播放通道,导致对端音频无法播放。

根据鸿蒙官方规范,AVSession(音视频会话) 是系统识别应用音频活跃状态的核心依据,系统检测到有效 AVSession 实例时,将保留音频播放资源,不执行回收策略。

3.2 实现方案

在通话页面生命周期中初始化 AVSession 实例,激活会话并配置基础信息,保证后台音频播放通道不被系统释放。

js 复制代码
import avSession from '@ohos.multimedia.avsession';

private session: avSession.AVSession | null = null;  // AVSession实例

async aboutToAppear() {
  await this.initAVSession();
}
// 实践中只创建session,切到后台音频播放就正常了
async initAVSession() {
  try {
    // 1. 创建会话 - 修正参数顺序和类型
    const context = getContext(this) as common.UIAbilityContext;
    let session = await avSession.createAVSession(context, 'SESSION_NAME', 'audio');
    this.session = session;
    // 2. 激活会话
    await this.session.activate();
    // 3. 设置元数据
    const metadata: avSession.AVMetadata = {
      assetId: 'audio_123',
      title: '我的音频',
      artist: 'Artist Name',
    };
    await this.session.setAVMetadata(metadata);
    // 4. 设置播放状态
    const playbackState: avSession.AVPlaybackState = {
      state: avSession.PlaybackState.PLAYBACK_STATE_PLAY,
      speed: 1.0,
      position: { elapsedTime: 0, updateTime: Date.now() }
    };
    await this.session.setAVPlaybackState(playbackState);

    console.log('AVSession initialized successfully');

  } catch (err) {
    console.error(`AVSession Error: ${(err as BusinessError).message}`);
  }
}
相关推荐
南村群童欺我老无力.6 小时前
鸿蒙PC开发的Scroll组件maxHeight属性不存在
华为·harmonyos
Swift社区10 小时前
鸿蒙游戏多设备发布流程详解
游戏·华为·harmonyos
以太浮标10 小时前
华为eNSP模拟器综合实验之- 主机没有配置缺省网关时,通过路由式Proxy ARP实现通信(arp-proxy enable)
运维·网络·网络协议·华为·智能路由器·信息与通信
Goway_Hui12 小时前
【ReactNative鸿蒙化-三方库使用与C-API集成】
c语言·react native·harmonyos
nashane13 小时前
HarmonyOS 6.0 分布式相机实战:调用远端设备摄像头与AI场景识别(API 11+)
分布式·数码相机·harmonyos·harmonyos 5
Huanzhi_Lin13 小时前
鸿蒙NEXT出包
华为·harmonyos·鸿蒙·harmony·鸿蒙next·hap
luoganttcc14 小时前
华为昇腾(Ascend)等芯片,同样存在“寄存器 / 片上存储资源限制并发”的问题
算法·华为
╰つ栺尖篴夢ゞ14 小时前
HarmonyOS Next面试题之主线程与子线程访问同一个单例,获取的对象是同一个吗?
单例模式·多线程·harmonyos·sendable·actor模型·内存隔离
Black蜡笔小新14 小时前
国标GB28181视频监控平台EasyCVR赋能平安乡村建设,构筑乡村治理“数字防线”
java·网络·音视频
中小企业实战军师刘孙亮15 小时前
组织赋能+体系搭建,破解中小企业增长困局-佛山鼎策创局破局增长咨询
架构·产品运营·音视频·制造·业界资讯