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

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

一、方案概述

在实时音频通话场景中,应用切换至后台、设备息屏后,进程保活、音频采集与音频播放的连续性是核心体验指标。本方案基于鸿蒙系统 ** 长时任务(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}`);
  }
}
相关推荐
美狐美颜SDK开放平台3 小时前
直播美颜SDK选型避坑指南:功能与性能对比
人工智能·音视频·美颜sdk·直播美颜sdk·短视频美颜sdk
zzialx3 小时前
HarmonyOS:照片添加多样式的水印信息
华为·harmonyos
CDN3604 小时前
CDN 无法播放音视频?流媒体回源与 Range 配置修复
运维·音视频
左手厨刀右手茼蒿4 小时前
Flutter 三方库 k_m_logic 的鸿蒙化适配指南 - 构建声明式的业务逻辑状态机、助力鸿蒙端复杂交互流程的解耦与重构
flutter·harmonyos·鸿蒙·openharmony·k_m_logic
钛态4 小时前
Flutter 三方库 result_type 深入鸿蒙强类型返回栈跨界交互适配:肃清空指针回调与运行时崩溃、大幅增注接口安全壁垒且提升多隔离桥接数据抛出健壮性-适配鸿蒙 HarmonyOS ohos
flutter·交互·harmonyos
lxysbly4 小时前
鸿蒙街机模拟器app下载
华为·harmonyos
左手厨刀右手茼蒿4 小时前
Flutter 三方库 invertible 的鸿蒙化适配指南 - 实现极致的撤销与重做流、助力鸿蒙端高交互编辑类应用开发
flutter·harmonyos·鸿蒙·openharmony
加农炮手Jinx4 小时前
Flutter 三方库 fast_i18n 的鸿蒙化适配指南 - 掌握类型安全的国际化编译技术、助力鸿蒙应用构建全球化且极速响应的多语言交互体系
flutter·harmonyos·鸿蒙·openharmony·fast_i18n