uniapp实现音频播放抢占系统音频焦点

news2024/9/27 19:19:28

项目为使用uniapp框架开发的Android/iOS APP应用

实现功能需求

假设手机正在播放音乐,当前APP处于前台收到消息,需播放提示音提示用户。目标为降低后台正在播放音乐的音量,播放提示音,播放完毕后恢复后台音乐音量

需求分析

乍一看,需求看似很简单,实则以目前uni官方所封装的API根本无法实现,附上链接uni.createInnerAudioContext,该API中的sessionCategory配置,实则为当前APP的音频模式,播放的时候影响到其他APP的行为只有一个结果——暂停
很明显,这并非我们能接受的结果,只能另寻他路,既然uni不行,那就只能靠native.js调用原生方法了

查阅文档

博主找遍了uni官方的文档、论坛,找不到任何相关的功能实现文章。无奈,只能查原生文档,Android、iOS,在阅读了大量原生实现文章后,注意到一个关键词——音频焦点,就是说后续的编码均是围绕着音频焦点而开展的

Android端

Android端的实现,需要使用到AudioManagerMediaPlayerAudioAttributes这三个类,首先AudioManager实例需要使用Context去获取
AudioManager类
MediaPlayer类则直接import后new一个新实例就可以了,AudioAttributes则用来设置所播放音频的属性。最后,还有一个音频播放结束后释放音频焦点的监听方法,这个在Java中为Interface接口方法——MediaPlayer.OnCompletionListener
参考链接:AudioManager类、MediaPlayer类、AudioAttributes类、
MediaPlayer.OnCompletionListener接口

<script>
export default {
	data() {
		return {
			audioManager: null, // 音频管理
			innerAudioContext: {}, // 播放器实例
			audioFocus: false // 音频焦点
		}
	},
	created() {
		this.buildAudio();
	},
	methods: {
		buildAudio() {
			let path = plus.io.convertLocalFileSystemURL('xxx/voice.mp3');
			if (this.$store.state.platform == 'android') {
				// Android端
				let main = plus.android.runtimeMainActivity(); // 获取应用主Activity实例对象
				let Context = plus.android.importClass('android.content.Context'); // 全局上下文
				this.audioManager = main.getSystemService(Context.AUDIO_SERVICE);
				let MediaPlayer = plus.android.importClass('android.media.MediaPlayer');
				let AudioAttributes = plus.android.importClass('android.media.AudioAttributes');
				this.innerAudioContext = new MediaPlayer();
				// Android中的接口实现使用plus.android.implements,注意接口名称类与接口方法之间要用符号$连接
				let event = plus.android.implements('android.media.MediaPlayer$OnCompletionListener', {
					onCompletion: () => {
						if (this.audioFocus) {
							this.audioManager.abandonAudioFocus(null); // 放弃音频焦点
							this.audioFocus = false;
						}
					}
				});
				this.innerAudioContext.setOnCompletionListener(event); // 设置播放完毕监听
				this.innerAudioContext.setDataSource(path); // 设置播放器播放音频
				this.innerAudioContext.setAudioAttributes(AudioAttributes.CONTENT_TYPE_SONIFICATION); // 设置音频属性,CONTENT_TYPE_SONIFICATION为短暂提示音
				this.innerAudioContext.prepare();
			} else {
				// iOS端...
			}
		}
	}
}
</script>

需要播放音频时,则判断一下后台是否播放着音乐,若正在播放,则请求音频焦点,设置系统类型为音乐类型(STREAM_MUSIC),设置持续时间提示为短暂打断模式(AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
requestAudioFocus

if (this.$store.state.platform === 'android') {
	// Android端
	if (this.audioManager.isMusicActive()) {
		// 判断是否正在播放音乐
		this.audioFocus = true;
		this.audioManager.requestAudioFocus(null, this.audioManager.STREAM_MUSIC, this.audioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK); // 请求音频聚焦
	}
	this.innerAudioContext.start(); // 播放
} else {
	// iOS端...
}

iOS端

iOS端的实现,需要用到AVAudioSessionNSDataAVAudioPlayer这三个类,其中AVAudioSession需要使用sharedInstance获取分享音频会话实例
sharedInstance
NSData用于转换本地文件路径为二进制数据,供AVAudioPlayer类实例用于初始化播放器。最后,还需要挂载播放完毕的代理方法AVAudioPlayerDelegate,用于在音频播放结束后设置音频会话激活状态(相当于Android端的释放音频焦点)为false
参考链接:AVAudioSession类、AVAudioPlayer类、NSData类、
AVAudioPlayerDelegate代理

<script>
export default {
	methods: {
		buildAudio() {
			let path = plus.io.convertLocalFileSystemURL('xxx/voice.mp3');
			if (this.$store.state.platform == 'android') {
				// Android端...
			} else {
				// iOS端
				let AVAudioSession = plus.ios.importClass('AVAudioSession');
				this.audioManager = AVAudioSession.sharedInstance(); // 获取分享音频会话实例
				/**
				 * 设置音频会话类型为AVAudioSessionCategoryPlayback,同时配置选项为AVAudioSessionCategoryOptionMixWithOthers和AVAudioSessionCategoryOptionDuckOthers
				 * Constants that specify optional audio behaviors. setCategory:withOptions:error
				 * setCategory:(AVAudioSessionCategory)category:
				 * @param AVAudioSessionCategoryPlayback The category for playing recorded music or other sounds that are central to the successful use of your app.
				 * withOptions:(AVAudioSessionCategoryOptions)options:
				 * @param AVAudioSessionCategoryOptionMixWithOthers = 0x1 An option that indicates whether audio from this session mixes with audio from active sessions in other audio apps.
				 * @param AVAudioSessionCategoryOptionDuckOthers = 0x2 An option that reduces the volume of other audio sessions while audio from this session plays.
				 */
				this.audioManager.setCategorywithOptionserror('AVAudioSessionCategoryPlayback', 0x1 | 0x2, null);
				let NSData = plus.ios.importClass('NSData');
				let AVAudioPlayer = plus.ios.importClass('AVAudioPlayer');
				let pathFileData = NSData.dataWithContentsOfFile(path); // 音频文件转为二进制数据
				this.innerAudioContext = new AVAudioPlayer(); // new一个音频播放器实例
				this.innerAudioContext.initWithDataerror(pathFileData, null); // 初始化数据
				// iOS中挂载代理方法使用plus.ios.implements,注意挂载的方法名需一个字符不差写完整audioPlayerDidFinishPlaying:successfully:
				let delegate = plus.ios.implements('AVAudioPlayerDelegate', {
					'audioPlayerDidFinishPlaying:successfully:': () => {
						if (this.audioFocus) {
							/**
							 * 设置音频会话激活状态(相当于Android端的音频焦点)为false,同时配置选项为AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation,用于通知其他被中断的APP恢复播放
							 * Activates or deactivates your app’s audio session using the specified options. setActive:withOptions:error
							 * setActive:(BOOL)active:
							 * Specify YES to activate your app’s audio session, or NO to deactivate it.(Objective-C中的YES相当于JS的true,NO相当于false)
							 * withOptions:(AVAudioSessionSetActiveOptions)options:
							 * @param AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation = 1 An option that indicates that the system should notify other apps that you’ve deactivated your app’s audio session.
							 */
							this.audioManager.setActivewithOptionserror(false, 1, null);
							this.audioFocus = false;
						}
					}
				});
				this.innerAudioContext.plusSetAttribute('delegate', delegate); // 使用plusSetAttribute设置实例对象属性delegate
			}
		}
	}
}
</script>

接下来是音频播放,跟Android端差不多,iOS端这边是激活音频会话

if (this.$store.state.platform === 'android') {
	// Android端...
} else {
	// iOS端
	if (this.audioManager.plusGetAttribute('isOtherAudioPlaying')) {
		// 获取判断音频会话属性isOtherAudioPlaying
		this.audioFocus = true;
		this.audioManager.setActiveerror(true, null); // 激活音频会话
	}
	this.innerAudioContext.play(); // 播放
}

总结

编码完毕,实际上手调试,在后台其他APP(如音乐播放器)正在播放音乐时,本APP播放提示音,在Android/iOS两端都是表现为降低音乐音量,播放提示音,提示音播放完毕后,恢复音乐音量。
至此,已完美完成需求,这里简单说一下本功能的开发感受:uni官方没有实现的功能,确实通过H5+plus绝大部分功能都是可以实现的,但对于没人写过参考的功能,就只能靠自己查阅原生开发文档、查阅原生开发相关代码,之后尝试使用native.js改写,这整套流程下来,对一个纯前端开发而言确实不容易。但是,博主认为这个过程是收获颇丰的,也是让人有成就感的,毕竟全网第一份。
就说这么多了,Keep learning…

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/129559.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

拉伯证券|新能源汽车前11月产销翻倍,渗透率升至三分之一

2022年11月&#xff0c;国内新能源轿车渗透率已升至33.8%&#xff0c;创前史新高。 2022年的最终一个交易日早盘&#xff0c;两市高开高走&#xff0c;沪指涨0.61%&#xff0c;深证成指涨0.35%&#xff0c;创业板指涨0.3%。板块上来看&#xff0c;Web3.0、虚拟人、网络游戏概念…

BI技巧丨RankxYoY

群友&#xff1a;PowerBI可以实现两年的排名差异么&#xff1f; 白茶&#xff1a;可以&#xff01;安排&#xff01; 本期的问题&#xff0c;来自于群内小伙伴的一个实际应用场景。在实际业务中&#xff0c;这个需求属于常见类型&#xff0c;展示当前排名时&#xff0c;用户关注…

【软件测试】 测试开发?看看一线大厂需求的测试开发技能......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 能在一线大厂工作是…

FPGA知识汇集-FPGA系统时序理论

时序约束条件 下面来具体讨论一下系统时序需要满足的一些基本条件。我们仍然以下图的结构为例&#xff0c;并可以据此画出相应的时序分析示意图。 在上面的时序图中&#xff0c;存在两个时序环&#xff0c;我们称实线的环为建立时间环&#xff0c;而虚线的环我们称之为保持时间…

Java死锁

一.死锁是什么&#xff1f; 死锁指两个或者两个以上的线程在执行过程中&#xff0c;去争夺同样一个共享资源&#xff0c;造成的相互等待的现象&#xff0c;如果没有外部干预&#xff0c;线程会一直阻塞&#xff0c;无法往下执行&#xff0c;这样一直处于相互等待资源的线程叫做…

极米Z6X Pro怎么样?极米Z6X Pro亮度如何?极米Z6X Pro值得入手吗?

投影仪不知怎么选&#xff1f;不妨看看极米Z6X Pro&#xff0c;半山黛青的全新配色下&#xff0c;是仅有5.3cm的轻薄机身&#xff0c;1.4kg的重量&#xff0c;一只手就可以轻松拿取。相比同价位的投影产品&#xff0c;极米Z6X Pro摆放方便又不占地&#xff0c;外观时尚精致&…

Go Map

Go Map map 是一种key-value的键值对存储结构&#xff0c;其中key不能重复&#xff0c;无序。底层是hmap结构&#xff0c;hmap中维护buckets(bmap结构)。结构定义 type hmap struct {count int // 元素个数B uint8 // buckets已扩容的次数&#xff0c;buckets长…

JVM-Java内存区域

1、运行时数据区域 运行时数据区域&#xff1a;程序计数器、Java虚拟机栈、本地方法栈、堆、方法区。 非运行时数据区域&#xff1a;直接内存。 &#xff08;1&#xff09;程序计数器 字节码解释器通过改变程序计数器来依次读取指令&#xff0c;从而实现代码的流程控制。在多线…

谷歌2022年最受欢迎Chrome浏览器扩展程序:包含Tango,Compose AI等

根据截图排序从左到右依次介绍如下&#xff1a; RoPro&#xff1a;Roblox.com网站体验增加了许多有用且独特的功能&#xff1b; MyBib&#xff1a;自动创建 APA 样式、MLA 格式和哈佛引用样式引文&#xff1b; eJOY English&#xff1a;翻译自Netflix, Youtube, iFlix上的字…

Codeforces Round #841 (Div. 2) and Divide by Zero 2022(A-D)

Codeforces Round #841 (Div. 2) and Divide by Zero 2022&#xff08;A-D&#xff09; 题目链接限制AJoey Takes Moneystandard input/output1 s, 256 MBBKill Demodogsstandard input/output1 s, 256 MBCEven Subarraysstandard input/output2.5 s, 256 MBDValiant’s New M…

数字经济指标构建-各省、地级市匹配上市公司数据、城市数字化指数

一、中国城市数字经济指数2017-2022年&#xff08;数据代码报告&#xff09; 中国城市数字化指数全面覆盖城市数字化规划、建设、运营的各个方面&#xff0c;能够有效评估城市各个领域的数字化建设水平和运营效果。 全国城市数字经济发展热图 数据来源&#xff1a;中国城市数字…

语音转换之CycleGan-VC2:原理与实战

非平行语音转换CycleGAN 之前学习了传统统计学习里的经典的语音转换模型GMM。随着深度学习的发展&#xff0c;出现了更好的语音转换方法&#xff0c;今天学习较为经典的CycleGan。 平行语音转换一般流程 典型代表就是基于GMM的语音转换。平行数据就是说源语音和目标语音一一…

矢量图斑局部狭长判断和定位局部狭长部分(PostGIS、Java、C#实现)

矢量数据在数据采集过程中由于数据处理导致出现局部狭窄的面状部分&#xff0c;如下图 1. 定义和解决方法 狭长结构是指图斑几何形态上窄而长的部分,符号化后出现图形粘连压盖现象,导致难以在图面上清晰地表达出来。因此,依据地图表达比例尺因素需要对狭长结构进行融解处理。在…

helm部署frps和连接

文章目录一. helm部署frps1.1 下载1.2 部署1.2.1 不开启dashboard界面1.2.2 开启dashboard界面1.2.3 卸载1.3 查看1.4 IP Port 允许端口1.5 Web Ingress二. frpc客户端连接2.1 IP Port 连接2.2 Web 域名连接一. helm部署frps 1.1 下载 mkdir -p /root/i/helm && cd…

并查集专题1_图篇

1.并查集介绍 并查集支持查询和合并操作&#xff0c;只回答两个节点是不是在一个连通分量中&#xff0c;并不回答路径问题。 如果一个问题具有传递性&#xff0c;可以考虑用并查集。并查集最常见的一种设计思想是把在同一个连通分量中的节点组织成一个树形结构。 2.并查集的…

动作捕捉技术应用于地面移动机器人协同

《一千零一夜》故事集中收录的《阿拉丁神灯》深受读者们的喜爱&#xff0c;其中阿拉丁拥有一块神奇的魔毯&#xff0c;它具有运载功能&#xff0c;可以将物体轻松便捷的从一个地方转移到另一个地方。在现实生活中&#xff0c;可变形布作为一种轻量便携且具有良好适应性的载体&a…

redhat9中mysql常用命令(持续更新)

目录 1、查看当前用户 2、查看选择的数据库 3、创建数据库 4、创建数据表 5、插入数据 6、查看表所以字段的内容 7、查看数据库当前密码策略 8、查看密码插件 9、更改密码策略为LOW&#xff0c;改为LOW或0 10、 更改密码长度 11、设置大小写、数字和特殊字符均不要求 …

微服务系列专栏介绍

文章目录一 专栏介绍1.1 微服务行业背景不同行业IT系统更新频率IT系统存在的问题微服务架构在企业中应用情况1.2 什么是微服务1.3 微服务的特点1.4 微服务诞生背景1.5 微服务架构的优势二 专栏目标三 专栏涉及技术四 专栏架构1.微服务架构&#xff1a;2.Go语言3.go-micro架构4.…

入侵无线WiFi的主要方式及防护要点

从攻击形态上看&#xff0c;无线网络攻击主要可以分为三个大类&#xff1a;被动型攻击、主动型攻击以及针对网络组件的攻击&#xff1a; 被动攻击一般发生在攻击者处于无线网络范围内并可以监视无线通信内容时&#xff0c;最常见的被动攻击是数据包嗅探。由于被动攻击者只是监…

HTTPS 的通信加解密过程,证书为什么更安全?

经典面试题 HTTPS 的通信加解密过程&#xff0c;证书为什么更安全&#xff1f; 考察点 《计算机网络》相关知识 了解 HTTPS 协议加解密的过程 了解数字证书认证的过程 技术点 对称加密和非对称加密 HTTPS 协议的加解密过程 数字证书认证过程 对称加密和非对称加密 对称加…