解决Agora声网音视频在后台没有声音的问题

2023-10-29

前言:本文会介绍 Android 与 iOS 两个平台的处理方式

一、Android高版本在应用退到后台时,系统为了省电会限制应用的后台活动,因此我们需要开启一个前台服务,在前台服务中发送常驻任务栏通知,以此来保证App 退到后台时不会被限制活动.

前台服务代码如下:

package com.notify.test.service;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;

import com.notify.test.R;

import androidx.annotation.Nullable;

/**
 * desc:解决声网音视频锁屏后听不到声音的问题
 * (可以配合Application.ActivityLifecycleCallbacks使用)
 *
 * Created by booyoung
 * on 2023/9/8 14:46
 */
public class KeepAppLifeService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private final String notificationId = "app_keep_live";
    private final String notificationName = "audio_and_video_call";

    @Override
    public void onCreate() {
        super.onCreate();
        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        //创建NotificationChannel
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(notificationId, notificationName, NotificationManager.IMPORTANCE_HIGH);
            //不震动
            channel.enableVibration(false);
            //静音
            channel.setSound(null, null);
            notificationManager.createNotificationChannel(channel);
        }
        startForeground(1, getNotification());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        //stop service
        this.stopSelf();
    }

    /**
     * 获取通知(Android8.0后需要)
     * @return
     */
    private Notification getNotification() {
        Notification.Builder builder = new Notification.Builder(this)
                .setSmallIcon(R.mipmap.logo)
                .setOngoing(true)
                .setContentTitle("App名称")
                .setContentIntent(getIntent())
                .setContentText("音视频通话中,轻击以继续");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            builder.setChannelId(notificationId);
        }
        return builder.build();
    }

    /**
     * 点击后,直接打开app
     * @return
     */
    private PendingIntent getIntent() {
        //获取启动Activity
        Intent msgIntent = getApplicationContext().getPackageManager().getLaunchIntentForPackage(getPackageName());
        PendingIntent pendingIntent = PendingIntent.getActivity(
                getApplicationContext(),
                1,
                msgIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        return pendingIntent;
    }
}

不要忘记在AndroidManifest.xml中声明Service哈

 <service android:name=".service.KeepAppLifeService"
      android:enabled="true"
      android:exported="false"
      android:stopWithTask="true" />

然后接下来就需要在声网音视频接通与挂断分别开启与关闭前台服务了,此处回调使用了EaseCallKit的写法,如果没使用EaseCallKit UI库的可以自己在EaseVideoCallActivity中的接通与挂断回调开启与关闭前台服务

  public void addCallkitListener() {
        callKitListener = new EaseCallKitListener() {
            @Override
            public void onInviteUsers(Context context, String userId[], JSONObject ext) {
            }

            @Override
            public void onEndCallWithReason(EaseCallType callType, String channelName, EaseCallEndReason reason, long callTime) {
                EMLog.d(TAG, "onEndCallWithReason" + (callType != null ? callType.name() : " callType is null ") + " reason:" + reason + " time:" + callTime);
                SimpleDateFormat formatter = new SimpleDateFormat("mm:ss");
                formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
                String callString = "通话时长";
                callString += formatter.format(callTime);

                Toast.makeText(MainActivity.this, callString, Toast.LENGTH_SHORT).show();
                //关闭任务栏通知
                stopBarNotify();
            }

            @Override
            public void onGenerateToken(String userId, String channelName, String appKey, EaseCallKitTokenCallback callback) {
                EMLog.d(TAG, "onGenerateToken userId:" + userId + " channelName:" + channelName + " appKey:" + appKey);
                //获取声网Token
                getAgoraToken(userId, channelName, callback);
               //创建服务开启任务栏通知(此处为了模拟,最好将openBarNotify()方法放在获取成功声网token后调用)
                openBarNotify();
            }

            @Override
            public void onReceivedCall(EaseCallType callType, String fromUserId, JSONObject ext) {
                EMLog.d(TAG, "onRecivedCall" + callType.name() + " fromUserId:" + fromUserId);
            }

            @Override
            public void onCallError(EaseCallKit.EaseCallError type, int errorCode, String description) {
                EMLog.d(TAG, "onCallError");
            }

            @Override
            public void onInViteCallMessageSent() {
//                LiveDataBus.get().with(DemoConstant.MESSAGE_CHANGE_CHANGE).postValue(new EaseEvent(DemoConstant.MESSAGE_CHANGE_CHANGE, EaseEvent.TYPE.MESSAGE));
            }

            @Override
            public void onRemoteUserJoinChannel(String channelName, String userName, int uid, EaseGetUserAccountCallback callback) {

            }
        };
        EaseCallKit.getInstance().setCallKitListener(callKitListener);
    }



  private void openBarNotify(){
        keepAppIntent = new Intent(this, KeepAppLifeService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //android8.0以上通过startForegroundService启动service
            startForegroundService(keepAppIntent);
        } else {
            startService(keepAppIntent);
        }
    }

    private void stopBarNotify(){
        if (keepAppIntent != null) {
            stopService(keepAppIntent);
        }
    }


二、iOS想在后台时播放声音,需要在添加App plays audio or streams audio/video using AirPlay权限

1.Info.plist里找到选项Required background modes 添加App plays audio or streams audio/video using AirPlay

2.在Signing&Capabilities -> Background Modes -> 勾选 Audio,AirPlay, and Picture in Picture

3.在AppDelegate.m中实现applicationDidEnterBackground代理方法

- (void)applicationDidEnterBackground:(UIApplication *)application{
    //环信已实现了进入后台的处理逻辑,如果要自己处理,可以参考下边注释代码
    [[EMClient sharedClient] applicationDidEnterBackground:application];
}

#if Ease_UIKIT
//    - (void)applicationDidEnterBackground:(NSNotification *)notification {
//        if (!self.config.shouldRemoveExpiredDataWhenEnterBackground) {
//            return;
//        }
//        Class UIApplicationClass = NSClassFromString(@"UIApplication");
//        if(!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) {
//            return;
//        }
//        UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)];
//        __block UIBackgroundTaskIdentifier bgTask = [application beginBackgroundTaskWithExpirationHandler:^{
//            // Clean up any unfinished task business by marking where you
//            // stopped or ending the task outright.
//            [application endBackgroundTask:bgTask];
//            bgTask = UIBackgroundTaskInvalid;
//        }];
//
//        // Start the long-running task and return immediately.
//        [self deleteOldFilesWithCompletionBlock:^{
//            [application endBackgroundTask:bgTask];
//            bgTask = UIBackgroundTaskInvalid;
//        }];
//    }
#endif

 4.因为App plays audio or streams audio/video using AirPlay权限只能是音乐播放类与具有音视频通话场景的App使用,所以审核的时候需要在备注描述清楚使用该场景的方式.如果审核失败,可以录制视频在附件上传,然后等待苹果重新审核即可.如果录制的视频没有问题,那就坐等着审核通过了,good luck!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

解决Agora声网音视频在后台没有声音的问题 的相关文章

  • 检查 Firebase 邀请是否引导至 Play 商店

    当在 Android 上使用 Firebase 邀请并在应用程序启动时访问动态链接时 有没有办法知道用户是通过邀请刚刚安装了该应用程序还是已经安装了该应用程序 非常感谢 Borja 编辑 感谢 Catalin Morosan 的回答 事实证
  • 使用 appcelerator titan 在 android 中后退按钮退出应用程序

    我是钛开发的新手 在本机 android 中 如果我们按下后退按钮 则仅当前活动将被关闭 并且它将返回到上一个活动 但是当我使用 Titanium 在 Android 中按下后退按钮时 它会从应用程序退出 我怎样才能改变这种行为 有两种类型
  • iOS:在代码中访问 app-info.plist 变量

    我正在开发通用应用程序 并且希望访问代码中 app info plist 文件中存储的值 原因 我使用以下方法从故事板动态实例化 UIViewController UIStoryboard storyboard UIStoryboard s
  • 如何从另一个应用程序向一个应用程序添加视图

    我的应用程序叫做我的好应用 MyNiceApp 主要只是一个加载视图的核心coreView在主活动中onCreate coreView由用户根据需要下载的其他插件的视图填充 我定义了核心视图上的各个区域 这些区域可以通过 MyNiceApp
  • 如何使用RecyclerView.State保存RecyclerView滚动位置?

    我有一个关于 Android 的问题RecyclerView State http developer android com reference android support v7 widget RecyclerView State h
  • Android:每秒更新蓝牙 RSSI

    我试图每秒显示蓝牙信号强度 RSSI Timer 来自检测到的设备 但我无法调用onRecive 多次因为接收器生命周期 http developer android com reference android content Broadc
  • 关于 S3 文件传输的权限

    我正在使用 S3TransferManager Sample 进行测试 我创建了Cognito并设置了IAM并最后更改了constants swift文件 我上传没有问题 但下载失败 错误信息是 下载失败 错误域 com amazonaws
  • 关系上的核心数据属性?

    我刚刚开始使用核心数据 我有一个Headache实体和一个Medication实体 头痛和药物之间存在多对多的关系 当您加重头痛时 您可以选择多种药物 我希望能够指定这些药物的数量 我对 MySQL 更熟悉 您可以在其中创建一个数据透视表
  • 为什么设置 MediaRecorder 时显示错误 IllegalStateException?

    我的代码设置 MediaRecorder 它显示行集质量错误 mMediaRecorder new MediaRecorder Step 1 Unlock and set camera to MediaRecorder mCamera st
  • android sqlite 如果不存在则创建表

    创建新表时遇到一点问题 当我使用 CREATE TABLE 命令时 我的新表按应有的方式形成 但是当我退出活动时 应用程序崩溃 并且我在 logcat 中得到一个表已存在 如果我使用 CREATE TABLE IF NOT EXISTS 则
  • 上次更新arm64-v8a后,模拟器在M1 Mac上显示离线

    昨天模拟器运行得很好 系统镜像为arm64 v8a 我不太记得工作系统映像的版本名称 但是昨天我接受了 ARM 64 系统映像的更新 版本名称是 S 我可以从 AVD 管理器启动模拟器 当我按 运行应用程序 按钮时模拟器也会启动 但是 该应
  • Android 简单 TextView 动画

    我有一个 TextView 我想倒计时 3 2 1 发生了事情 为了使其更有趣 我希望每个数字都以完全不透明开始 然后淡出至透明 有没有一种简单的方法可以做到这一点 尝试这样的事情 private void countDown final
  • 如何支持滑动删除具有组合布局的 UICollectionView 列表中的行?

    以前对于表视图 这是在UITableViewDataSource委托回调tableView commit forRowAt 相关 API 中是否有等效功能新的集合视图 https developer apple com documentat
  • stringFromDate 始终为 NIL

    我知道这是一个重复的问题 但是在 stackoverflow 和 google 上搜索了许多类似的问题后 没有一个解决方案对我有用 我正在尝试将从数据库收到的日期转换为字符串格式以在 iPhone 应用程序中显示 我正在按以下方式将日期转换
  • 如何使用 Expo 播放 Youtube 视频

    我正在尝试使用反应本机 YouTube 组件 https github com inProgress team react native youtube在我的世博项目中 但是 我无法让它发挥作用 到目前为止 我所拥有的只是带有红色边框的黑屏
  • 在React-native中,如何更改NavigatorIOS的样式

    在react native中 如何更改NavigatorIOS的样式 例如背景颜色 谢谢你 var speedNews React createClass render function return
  • CF 类型的带有 __attribute__((NSObject)) 的强 @property 不会保留

    更新 自 Xcode 4 6 起 此问题已得到修复 现在 这项技术再次按预期发挥作用 但是 在代码中使用之前 请务必阅读 Rob Napier 出色答案顶部的注释 原帖 ARC Xcode 4 3 1 iOS 5 1 我有一个 CF 类型
  • 将 Objective-C 框架 (CocoaPod) 导入 Swift?

    我正在尝试导入libjingle peerconnection框架到我的 Xcode 项目中 但由于某种原因 我无法使用以下命令导入 Objective C 标头import RTCICEServer在 Swift 源文件中 我尝试使用头文
  • 动画结束后更改视图位置

    我开发了一个基于ViewGroup我的问题是我需要在动画结束后保存项目的位置 我打了电话setFillAfter true 在我创建的动画对象中AnimationListener并在其中onAnimationEnd方法调用View layo
  • Android 使用非公历

    我正在创建一个DatePickerDialogFragment用户将在其中选择出生日期 我想确保我可以处理非公历日期 我无法更改在我的设备上使用的日历类型 Android 是否允许用户切换日历类型 如果是的话 步骤是什么 到目前为止我还没有

随机推荐

  • mac m2 安装使用linux

    问题出现在哪里
  • 图片转为git 小程序 python

    先展示效果图 下面展示一些 内联代码片 import os import imageio from natsort import natsorted def create gif image list gif name frames for
  • android 平板怎么刷机,小白必看,安卓平板电脑刷机教程之一键刷机

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 从来没刷机的的小白 你有以下疑问吗 什么是 刷机 什么是 ROM 什么是 ROOT 什么是 Recovery 什么是USB调试模式 如何打开USB调试模式 刷机会不会损坏我的设备 刷机中途能拔掉
  • 利用反射创建对象

    public class Reflection public static void main String args throws Exception Reflection reflection new Reflection System
  • HTML属性

    目录 HTML 属性 属性实例 更多 HTML 属性实例 属性例子 1 属性例子 2 属性例子 3 HTML 提示 使用小写属性 始终为属性值加引号 HTML 属性参考手册 一个完整的实例 属性为 HTML 元素提供附加信息 HTML 属性
  • 安装Firefly错误-Unable to find vcvarsall.bat

    晚上尝试安装Firefly时 在安装twisted时出现错误而退出 根据错误提示如下 error Setup script exited with error Microsoft Visual C 9 0 is required Unabl
  • 5、基于注解的AOP配置

    一 开启注解支持 1 概述 1 Spring AOP如同IoC一样支持基于XML和基于注解两种配置方式 基于注解所需的依赖和基于XML所需的依赖一致 其中spring context包含了Spring IoC Spring AOP等核心依赖
  • 改进的北方苍鹰算法优化VMD参数,最小包络熵、样本熵、信息熵、排列熵(适应度函数可自行选择,一键修改)包含MATLAB源代码...

    今天给大家带来一期由改进的北方苍鹰算法 SCNGO 优化VMD的两个参数 同样以西储大学数据集为例 选用105 mat中的X105 BA time mat数据中1000个数据点 没有数据的看这篇文章 西储大学轴承诊断数据处理 matlab免
  • linux之数据库操作,Linux之MySQL数据库常用操作

    8种机械键盘轴体对比 本人程序员 要买一个写代码的键盘 请问红轴和茶轴怎么选 因为最近在使用云服务器部署项目 所以也难免要在服务器上使用MySQL 所以就想把MySQL常使用记录下来 以便日后查看并熟悉 也希望能够对大家有所帮助 首先你需要
  • Linux内核TCP参数调优全面解读

    Linux内核TCP参数调优全面解读 前言 TCP 性能的提升不仅考察 TCP 的理论知识 还考察了对于操心系统提供的内核参数的理解与应用 TCP 协议是由操作系统实现 所以操作系统提供了不少调节 TCP 的参数 如何正确有效的使用这些参数
  • redis配置超时时间

    redis配置超时时间 cd usr local redis etc vim redis conf 找到timeout 0 改为 timeout 30
  • 在sublime text中用终端来编译运行java

    在sublime text中用终端来编译运行java sublime text是一款非常优秀的轻量编辑器 今天就来给大家演示下如何用sublime来编译运行java 安装Terminus 首先需要在sublime中安装terminus这个终
  • JAVA学习之——计算机网络(背诵版)

    1 简述OSI七层协议 物理层 主要解决两台物理机之间的通信 通过二进制比特流的传输来实现 二进制数据表现为电流电压上的强弱 到达目的地再转化为二进制机器码 网卡 集线器工作在这一层 在局部局域网上传送数据帧 它负责管理计算机通信设备和网络
  • 【转】C语言的学习路线

    http topic csdn net u 20110922 08 391f0557 6bbc 490d 8394 b7dede44fa0e html seed 1927482974 r 75671683 r 75671683 UNIX下C
  • Java中判断两个类是否相等

    Java中判断两个类是否相等 当有参数的类生成对象时 当两个对象给的参数相同时 会将第二个对象指向第一个对象的地址 如实例中展示 会输出true public class test1 public static void main Stri
  • GLSL 程序与使用

    核心模式OpenGL GLSL程序 GLSL程序简介和在QT中向GLSL程序变量传递数据 数据类型 包含基本数据类型 int float double uint bool 两种容器类型 向量 Vector 标识符 含义 vecn n个flo
  • 宏任务与微任务

    首先执行顺序 同步任务 gt 异步任务 异步任务又分为 宏任务与微任务 所以整个顺序为 同步任务 gt 微观任务 gt 宏观任务 微观任务大概有Promise then Object observe MutationObserver pro
  • shinelon笔记本进bios设置u盘启动_系统重装U盘启动进BIOS按键查询

    点击蓝字 关注我们 总的来讲 设置电脑从U盘启动一共有两种方法 第一种是开机时候按快捷键然后选择U盘启动 第二种进Bios然后设置U盘 PART ONE 一 U盘启动 组装机主板 品牌笔记本 品牌台式机 主板品牌 启动按键 笔记本品牌 启动
  • WIN32_FIND_DATA、FILETIME、FindFirstFile对文件的操作

    WIN32 FIND DATA FILETIME对文件的操作 include stdafx h include
  • 解决Agora声网音视频在后台没有声音的问题

    前言 本文会介绍 Android 与 iOS 两个平台的处理方式 一 Android高版本在应用退到后台时 系统为了省电会限制应用的后台活动 因此我们需要开启一个前台服务 在前台服务中发送常驻任务栏通知 以此来保证App 退到后台时不会被限