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

2023-09-14 17:23:02

前言:本文会介绍 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!

更多推荐

基于SSM+Vue的乐购游戏商城系统

末尾获取源码开发语言:JavaJava开发工具:JDK1.8后端框架:SSM前端:采用Vue技术开发数据库:MySQL5.7和Navicat管理工具结合服务器:Tomcat8.5开发软件:IDEA/Eclipse是否Maven项目:是目录一、项目简介二、系统设计三、系统项目截图管理员功能模块前台功能模块用户功能模块四、

Pycharm中画图警告:MatplotlibDeprecationWarning

前言:\textcolor{Green}{前言:}前言:💞这是由于在python中画图出现的问题,一般不会有错。因为它只是个警告,但是我们也可以知道解决这个问题的方法,防止后面出问题的时候知道怎么解决。前因后果问题:原因解决方法一解决方法二问题:在使用Matplotlib的show方法时,控制台出现以下警告信息:Ma

【Python】Python 包 ③ ( Python 第三方包简介 | 安装第三方包 | 命令行安装 | PyCharm 安装 | 设置代理 | 使用第三方包开发 )

文章目录一、Python第三方包简介二、安装第三方包1、使用pip命令安装第三方包2、指定第三方包安装版本3、设置代理4、PyCharm中安装第三方包三、使用第三方包开发一、Python第三方包简介Python包中包含了很多Python模块,每个Python模块对应一个Python源码,其中包含了若干功能(函数);Py

Nginx配置文件详解

一、nginx的配置文件1、常见的配置文件及其作用nginx常见配置文件位置:安装路径/conf目录中。[root@centos7nginx]#tree.├──conf│├──fastcgi.conf│├──fastcgi.conf.default│├──fastcgi_params│├──fastcgi_params

pycharm中恢复原始界面布局_常用快捷键_常用设置

文章目录1恢复默认布局1.1直接点击file→ManageIDESettings→RestoreDefaultSettings(如下图所示):1.2直接点击RestoreandRestart,然后Pycharm就会自动重启,重启之后的界面就是最原始的界面了2改变主题2.1在PyCharm的右上角,点击“齿轮,然后点击:

ThreadPoolExecutor的使用

1.在SpringBoot项目中使用ThreadPoolExecutor:SpringBoot中可以通过创建一个配置类来定义ThreadPoolExecutor,然后在需要使用的地方直接注入即可。@ConfigurationpublicclassThreadPoolConfig{@BeanpublicExecutora

【OpenSSL】VC编译OpenSSL

VC编译OpenSSL编译工具准备编译OpenSSL建立`HelloWorld`工程创建VS工程编译工具准备安装好VisualStudio。安装Perl,主要是用来生成nmake的。准备好汇编语言编译工具nasm,并添加到path路径。下载好OpenSSL源代码。编译OpenSSL安装Perl,并加入到path路径,检

Sentinel控制台配置 持久化到nacos

sentinel控制台,使用方便,功能强大。使用官方的jar包,配置不会持久化,sentinel重启后会导致,之前的规则全部丢失,下面一起改造源码实现规则数据的持久化sentinel源码地址(github访问太慢,直接上镜像版)Sentinel:Sentinel是什么随着微服务的流行,服务和服务之间的稳定性变得越来越重

深入了解代理服务器:Socks5、IP代理与网络安全

在当今数字化时代,网络安全和数据采集对于网络工程师和爬虫开发者来说至关重要。代理服务器是一项关键技术,为保护隐私、绕过访问限制和提高网络安全提供了有力工具。本文将深入探讨几种不同类型的代理服务器,包括Socks5代理、IP代理,以及它们在网络安全和爬虫开发中的作用。1.Socks5代理:全能的代理协议Socks5代理协

什么是网络安全?网络安全包括哪几个方面?

提及网络安全,很多人都是既熟悉又陌生,所谓的熟悉就是知道网络安全可以保障网络服务不中断。那么到底什么是网络安全?网络安全包括哪几个方面?通过下文为大家介绍一下。什么是网络安全?网络安全是指网络系统的硬件、软件及系统中的数据受到保护,不因偶然的或者恶意的原因而遭受到破坏、更改、泄露,系统连续可靠正常地运行,网络服务不中断

由于数字化转型对集成和扩展性的要求,定制化需求难以满足,百数低代码服务商该如何破局?

当政策、技术环境的日益成熟,数字化转型逐步成为企业发展的必选项,企业数字化转型不再是一道选择题,而是决定其生存发展的必由之路。通过数字化转型升级生产方式、管理模式和组织形式,激发内生动力,成为企业顺应时代变化,实现高质量发展的必然选择。一般来说,实现数字化转型的方式有3种:采购已有的标准系统、定制外包或者选购低代码平台

热文推荐