HarmonyOS 多音频播放并发政策及音频管理解析

news2024/9/21 22:41:07

音频打断策略

多音频并发,即多个音频流同时播放。此场景下,如果系统不加管控,会造成多个音频流混音播放,容易让用户感到嘈杂,造成不好的用户体验。为了解决这个问题,系统预设了音频打断策略,对多音频播放的并发进行管控,只有持有音频焦点的音频流才可以正常播放,避免多个音频流无序并发播放的现象出现。

当应用开始播放音频时,系统首先为相应的音频流申请音频焦点,获得焦点的音频流可以播放;若焦点申请被拒绝,则不能播放。在音频流播放的过程中,若被其他音频流打断,则会失去音频焦点。当音频流失去音频焦点时,只能暂停播放。在应用播放音频的过程中,这些动作均由系统自行完成,无需应用主动触发。但为了维持应用和系统的状态一致性,保证良好的用户体验,推荐应用监听音频打断事件,并在收到音频打断事件(InterruptEvent)时做出相应处理。

为满足应用对多音频并发策略的不同需求,音频打断策略预设了两种焦点模式,针对同一应用创建的多个音频流,应用可通过设置焦点模式,选择由应用自主管控或由系统统一管控。

音频打断策略决定了应该对音频流采取何种操作,如暂停播放、继续播放、降低音量播放、恢复音量播放等,这些操作可能由系统或应用来执行。音频打断策略预置了两种打断类型,用于区分音频打断事件(InterruptEvent)的执行者。

焦点模式

音频打断策略预设了两种焦点模式(InterruptMode):

● 共享焦点模式(SHARE_MODE):由同一应用创建的多个音频流,共享一个音频焦点。这些音频流之间的并发规则由应用自主决定,音频打断策略不会介入。当其他应用创建的音频流与该应用的音频流并发播放时,才会触发音频打断策略的管控。

● 独立焦点模式(INDEPENDENT_MODE):应用创建的每一个音频流均会独立拥有一个音频焦点,当多个音频流并发播放时,会触发音频打断策略的管控。

应用可以按需选择合适的焦点模式,在创建音频流时,系统默认采用共享焦点模式,应用可主动设置所需的模式。

设置焦点模式的方法:

● 若使用AVPlayer开发音频播放功能,则可以通过修改 AVPlayer 的audioInterruptMode属性进行设置。

● 若使用AudioRenderer开发音频播放功能,则可以调用 AudioRenderer 的setInterruptMode函数进行设置。

打断类型

音频打断策略(包括两种焦点模式)决定了应该对各个音频流采取何种操作,如暂停播放、继续播放、降低音量播放、恢复音量播放等。而针对这些操作的执行过程,根据执行者的不同,可以分为两种打断类型(InterruptForceType):

● 强制打断类型(INTERRUPT_FORCE):由系统进行操作,强制打断音频播放。

● 共享打断类型(INTERRUPT_SHARE):由应用进行操作,可以选择打断或忽略。

对于音频打断策略的执行,系统默认采用强制打断类型(INTERRUPT_FORCE),应用无法更改。但对于一些策略(如继续播放等),系统无法强制执行,所以这两种打断类型均可能出现。应用可根据音频打断事件(InterruptEvent)的成员变量 forceType 的值,获取该事件采用的打断类型。

在应用播放音频的过程中,系统自动为音频流执行申请焦点、持有焦点、释放焦点等动作,当发生音频打断事件时,系统强制对音频流执行暂停、停止、降低音量、恢复音量等操作,并向应用发送音频打断事件(InterruptEvent)回调。由于系统会强制改变音频流状态,为了维持应用和系统的状态一致性,保证良好的用户体验,推荐应用监听音频打断事件,并在收到音频打断事件(InterruptEvent)时做出相应处理。

对于一些系统无法强制执行的操作(例如音频流继续播放的场景),会向应用发送包含了共享打断类型的音频打断事件,由应用自行执行相应操作,此时应用可以选择执行或忽略,系统不会干涉。

监听音频打断事件

在应用播放音频时,推荐应用监听音频打断事件,当音频打断事件发生时,系统会根据预设策略,对音频流做出相应的操作,并针对状态发生改变的音频流,向所属的应用发送音频打断事件。

应用收到音频打断事件后,需根据其内容提示,做出相应的处理,避免出现应用状态与预期效果不一致的问题。

监听音频打断事件的方法:

● 若使用AVPlayer开发音频播放功能,则可以调用 AVPlayer 的on('audioInterrupt')函数进行监听,当收到音频打断事件(InterruptEvent)时,应用需根据其内容,做出相应的调整。

● 若使用AudioRenderer开发音频播放功能,则可以调用 AudioRenderer 的on('audioInterrupt')函数进行监听,当收到音频打断事件(InterruptEvent)时,应用需根据其内容,做出相应的调整。

为了带给用户更好的体验,针对不同的音频打断事件内容,应用需要做出相应的处理操作。此处以使用 AudioRenderer 开发音频播放功能为例,展示推荐应用采取的处理方法,提供伪代码供开发者参考(若使用 AVPlayer 开发音频播放功能,处理方法类似),具体的代码实现,开发者可结合实际情况编写,处理方法也可自行调整。

let isPlay; // 是否正在播放,实际开发中,对应与音频播放状态相关的模块
let isDucked; //是否降低音量,实际开发中,对应与音频音量相关的模块
let started; // 标识符,记录“开始播放(start)”操作是否成功

async function onAudioInterrupt(){
  // 此处以使用AudioRenderer开发音频播放功能举例,变量audioRenderer即为播放时创建的AudioRenderer实例。
  audioRenderer.on('audioInterrupt', async(interruptEvent) => {
    // 在发生音频打断事件时,audioRenderer收到interruptEvent回调,此处根据其内容做相应处理
    // 先读取interruptEvent.forceType的类型,判断系统是否已强制执行相应操作
    // 再读取interruptEvent.hintType的类型,做出相应的处理
    if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {
      // 强制打断类型(INTERRUPT_FORCE):音频相关处理已由系统执行,应用需更新自身状态,做相应调整
       switch (interruptEvent.hintType) {
        case audio.InterruptHint.INTERRUPT_HINT_PAUSE:
          // 此分支表示系统已将音频流暂停(临时失去焦点),为保持状态一致,应用需切换至音频暂停状态
          // 临时失去焦点:待其他音频流释放音频焦点后,本音频流会收到resume对应的音频打断事件,到时可自行继续播放
          isPlay = false; // 此句为简化处理,代表应用切换至音频暂停状态的若干操作
          break;
        case audio.InterruptHint.INTERRUPT_HINT_STOP:
          // 此分支表示系统已将音频流停止(永久失去焦点),为保持状态一致,应用需切换至音频暂停状态
          // 永久失去焦点:后续不会再收到任何音频打断事件,若想恢复播放,需要用户主动触发。
          isPlay = false; // 此句为简化处理,代表应用切换至音频暂停状态的若干操作
          break;
        case audio.InterruptHint.INTERRUPT_HINT_DUCK:
          // 此分支表示系统已将音频音量降低(默认降到正常音量的20%),为保持状态一致,应用需切换至降低音量播放状态
          // 若应用不接受降低音量播放,可在此处选择其他处理方式,如主动暂停等
          isDucked = true; // 此句为简化处理,代表应用切换至降低音量播放状态的若干操作
          break;
        case audio.InterruptHint.INTERRUPT_HINT_UNDUCK:
          // 此分支表示系统已将音频音量恢复正常,为保持状态一致,应用需切换至正常音量播放状态
          isDucked = false; // 此句为简化处理,代表应用切换至正常音量播放状态的若干操作
          break;
        default:
          break;
      }
    } else if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_SHARE) {
      // 共享打断类型(INTERRUPT_SHARE):应用可自主选择执行相关操作或忽略音频打断事件
      switch (interruptEvent.hintType) {
        case audio.InterruptHint.INTERRUPT_HINT_RESUME:
          // 此分支表示临时失去焦点后被暂停的音频流此时可以继续播放,建议应用继续播放,切换至音频播放状态
          // 若应用此时不想继续播放,可以忽略此音频打断事件,不进行处理即可
          // 继续播放,此处主动执行start(),以标识符变量started记录start()的执行结果
          await audioRenderer.start().then(async function () {
            started = true; // start()执行成功
          }).catch((err) => {
            started = false; // start()执行失败
          });
          // 若start()执行成功,则切换至音频播放状态
          if (started) {
            isPlay = true; // 此句为简化处理,代表应用切换至音频播放状态的若干操作
          } else {
            // 音频继续播放执行失败
          }
          break;
        default:
          break;
      }
   }
  });
}

播放音量管理

播放音量的管理主要包括对系统音量的管理和对音频流音量的管理。系统音量与音频流音量分别是指 HarmonyOS 系统的总音量和指定音频流的音量,其中音频流音量的大小受制于系统音量,管理两者的接口不同。

详细的 API 说明请参考audio API参考。

系统音量

管理系统音量的接口是 AudioVolumeManager,在使用之前,需要使用 getVolumeManager()获取 AudioVolumeManager 实例。目前该接口只能获取音量信息及监听音量变化,不能主动调节系统音量。

import audio from '@ohos.multimedia.audio';
let audioManager = audio.getAudioManager();
let audioVolumeManager = audioManager.getVolumeManager();
监听系统音量变化

通过设置监听事件,可以监听系统音量的变化:

audioVolumeManager.on('volumeChange', (volumeEvent) => {
  console.info(`VolumeType of stream: ${volumeEvent.volumeType} `);
  console.info(`Volume level: ${volumeEvent.volume} `);
  console.info(`Whether to updateUI: ${volumeEvent.updateUi} `);
});

音频流音量

管理音频流音量的接口是 AVPlayer 或 AudioRenderer 的 setVolume()方法,使用 AVPlayer 设置音频流音量的示例代码如下:

let volume = 1.0  // 指定的音量大小,取值范围为[0.00-1.00],1表示最大音量
avPlayer.setVolume(volume)

使用 AudioRenderer 设置音频流音量的示例代码如下:

audioRenderer.setVolume(0.5).then(data=>{  // 音量范围为[0.0-1.0]
  console.info('Invoke setVolume succeeded.');
}).catch((err) => {  
  console.error(`Invoke setVolume failed, code is ${err.code}, message is ${err.message}`);
});

音频播放流管理

对于播放音频类的应用,开发者需要关注该应用的音频流的状态以做出相应的操作,比如监听到状态为播放中/暂停时,及时改变播放按钮的 UI 显示。

读取或监听应用内音频流状态变化

参考使用AudioRenderer开发音频播放功能或audio.createAudioRenderer,完成 AudioRenderer 的创建,然后可以通过以下两种方式查看音频流状态的变化:

● 方法 1:直接查看 AudioRenderer 的state:

let audioRendererState = audioRenderer.state;
console.info(`Current state is: ${audioRendererState }`)

● 方法 2:注册 stateChange 监听 AudioRenderer 的状态变化:

audioRenderer.on('stateChange', (rendererState) => {
  console.info(`State change to: ${rendererState}`)
});

获取 state 后可对照AudioState来进行相应的操作,比如更改暂停播放按钮的显示等。

读取或监听所有音频流的变化

如果部分应用需要查询获取所有音频流的变化信息,可以通过 AudioStreamManager 读取或监听所有音频流的变化。

如下为音频流管理调用关系图:

在进行应用开发的过程中,开发者需要使用 getStreamManager()创建一个 AudioStreamManager 实例,进而通过该实例管理音频流。开发者可通过调用 on('audioRendererChange')监听音频流的变化,在音频流状态变化、设备变化时获得通知。同时可通过 off('audioRendererChange')取消相关事件的监听。另外,开发者可以主动调用 getCurrentAudioRendererInfoArray()来查询播放流的唯一 ID、播放流客户端的 UID、音频流状态等信息。

详细 API 含义可参考音频管理API文档AudioStreamManager。

开发步骤及注意事项

1.  创建 AudioStreamManager 实例。在使用 AudioStreamManager 的 API 前,需要使用 getStreamManager()创建一个 AudioStreamManager 实例。

import audio from '@ohos.multimedia.audio';
let audioManager = audio.getAudioManager();
let audioStreamManager = audioManager.getStreamManager();

2.  使用 on('audioRendererChange')监听音频播放流的变化。 如果音频流监听应用需要在音频播放流状态变化、设备变化时获取通知,可以订阅该事件。

audioStreamManager.on('audioRendererChange',  (AudioRendererChangeInfoArray) => {
  for (let i = 0; i < AudioRendererChangeInfoArray.length; i++) {
    let AudioRendererChangeInfo = AudioRendererChangeInfoArray[i];
    console.info(`## RendererChange on is called for ${i} ##`);
    console.info(`StreamId for ${i} is: ${AudioRendererChangeInfo.streamId}`);
    console.info(`Content ${i} is: ${AudioRendererChangeInfo.rendererInfo.content}`);
    console.info(`Stream ${i} is: ${AudioRendererChangeInfo.rendererInfo.usage}`);
    console.info(`Flag ${i} is: ${AudioRendererChangeInfo.rendererInfo.rendererFlags}`); 
    for (let j = 0;j < AudioRendererChangeInfo.deviceDescriptors.length; j++) {
      console.info(`Id: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].id}`);
      console.info(`Type: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].deviceType}`);
      console.info(`Role: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].deviceRole}`);
      console.info(`Name: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].name}`);
      console.info(`Address: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].address}`);
      console.info(`SampleRates: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].sampleRates[0]}`);
      console.info(`ChannelCount ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].channelCounts[0]}`);
      console.info(`ChannelMask: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].channelMasks}`);
    }
  }
});

3.  (可选)使用 off('audioRendererChange')取消监听音频播放流变化。

audioStreamManager.off('audioRendererChange');
console.info('RendererChange Off is called ');

4.  (可选)使用 getCurrentAudioRendererInfoArray()获取所有音频播放流的信息。该接口可获取音频播放流唯一 ID,音频播放客户端的 UID,音频状态以及音频播放器的其他信息。

说明

对所有音频流状态进行监听的应用需要申请权限ohos.permission.USE_BLUETOOTH,否则无法获得实际的设备名称和设备地址信息,查询到的设备名称和设备地址(蓝牙设备的相关属性)将为空字符串。

async function getCurrentAudioRendererInfoArray(){
  await audioStreamManager.getCurrentAudioRendererInfoArray().then( function (AudioRendererChangeInfoArray) {
    console.info(`getCurrentAudioRendererInfoArray  Get Promise is called `);
    if (AudioRendererChangeInfoArray != null) {s
      for (let i = 0; i < AudioRendererChangeInfoArray.length; i++) {
        let AudioRendererChangeInfo = AudioRendererChangeInfoArray[i];
        console.info(`StreamId for ${i} is: ${AudioRendererChangeInfo.streamId}`);
        console.info(`Content ${i} is: ${AudioRendererChangeInfo.rendererInfo.content}`);
        console.info(`Stream ${i} is: ${AudioRendererChangeInfo.rendererInfo.usage}`);
        console.info(`Flag ${i} is: ${AudioRendererChangeInfo.rendererInfo.rendererFlags}`);  
        for (let j = 0;j < AudioRendererChangeInfo.deviceDescriptors.length; j++) {
          console.info(`Id: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].id}`);
          console.info(`Type: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].deviceType}`);
          console.info(`Role: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].deviceRole}`);
          console.info(`Name: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].name}`);
          console.info(`Address: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].address}`);
          console.info(`SampleRates: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].sampleRates[0]}`);
          console.info(`ChannelCount ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].channelCounts[0]}`);
          console.info(`ChannelMask: ${i} : ${AudioRendererChangeInfo.deviceDescriptors[j].channelMasks}`);
        }
      }
    }
  }).catch((err) => {
    console.error(`Invoke getCurrentAudioRendererInfoArray failed, code is ${err.code}, message is ${err.message}`);
  });
}

音频输出设备管理

有时设备同时连接多个音频输出设备,需要指定音频输出设备进行音频播放,此时需要使用 AudioRoutingManager 接口进行输出设备的管理,API 说明可以参考AudioRoutingManager API文档。

创建 AudioRoutingManager 实例

在使用 AudioRoutingManager 管理音频设备前,需要先导入模块并创建实例。

import audio from '@ohos.multimedia.audio';  // 导入audio模块

let audioManager = audio.getAudioManager();  // 需要先创建AudioManager实例

let audioRoutingManager = audioManager.getRoutingManager();  // 再调用AudioManager的方法创建AudioRoutingManager实例

支持的音频输出设备类型

目前支持的音频输出设备见下表:

获取输出设备信息

使用 getDevices()方法可以获取当前所有输出设备的信息。

audioRoutingManager.getDevices(audio.DeviceFlag.OUTPUT_DEVICES_FLAG).then((data) => {
  console.info('Promise returned to indicate that the device list is obtained.');
});

监听设备连接状态变化

可以设置监听事件来监听设备连接状态的变化,当有设备连接或断开时触发回调:

// 监听音频设备状态变化
audioRoutingManager.on('deviceChange', audio.DeviceFlag.OUTPUT_DEVICES_FLAG, (deviceChanged) => {
  console.info('device change type : ' + deviceChanged.type);  // 设备连接状态变化,0为连接,1为断开连接
  console.info('device descriptor size : ' + deviceChanged.deviceDescriptors.length);
  console.info('device change descriptor : ' + deviceChanged.deviceDescriptors[0].deviceRole);  // 设备角色
  console.info('device change descriptor : ' + deviceChanged.deviceDescriptors[0].deviceType);  // 设备类型
});

// 取消监听音频设备状态变化
audioRoutingManager.off('deviceChange', (deviceChanged) => {
  console.info('Should be no callback.');
});

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

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

相关文章

微信群发消息的正确打开方式,让你的社交更高效!

在当今的社交媒体时代&#xff0c;微信已经成为了我们生活中必不可少的一部分。而微信的群发消息功能&#xff0c;让我们可以方便地将信息一次性发送给多个联系人。然而&#xff0c;微信的群发消息功能有一个限制&#xff0c;即每次只能群发200个联系人。这对于需要发送消息给大…

【网安大模型专题10.19】※论文5:ChatGPT+漏洞定位+补丁生成+补丁验证+APR方法+ChatRepair+不同修复场景+修复效果(韦恩图展示)

Keep the Conversation Going: Fixing 162 out of 337 bugs for $0.42 each using ChatGPT 写在最前面背景介绍自动程序修复流程Process of APR (automated program repair)1、漏洞程序2、漏洞定位模块3、补丁生成4、补丁验证 &#xff08;可以学习的PPT设计&#xff09;经典的…

Jmeter(十三):jmeter第三方插件管理工具安装详细步骤

jmeter第三方插件管理工具安装 第一步&#xff1a;下载 jmeter-plugins-manager-1.6.jar 第二步&#xff1a;把该jar包放置到:jmeter安装路径/lib/ext下 第三步&#xff1a;重启jmeter,在选项下可以看见插件管理 同时&#xff0c;我也准备了一份软件测试视频教程&#xff08;…

(10_24)【有奖体验】AIGC小说创作大赛开启!通义千问X函数计算部署AI助手

一个 AI 助手到底能做什么&#xff1f; 可以书写小说 可以解析编写代码 可以鼓舞心灵 提供职业建议 还有更多能力需要您自己去探索。接下来我们将花费 5 分钟&#xff0c;基于函数计算X通义千问部署一个 AI 助手&#xff0c;帮你撰写各类文案。 领取函数计算试用额度 首次开…

折纸问题

折纸的次数 —— 从上到下的折痕 本质上是中序遍历的问题&#xff0c;因为每一次在已有的折痕后折的时候&#xff0c;当前折痕上的折痕一定为凹&#xff0c;当前折痕下的折痕一定为凸 。实际模拟了一个不存在的二叉树结构的中序遍历。 注&#xff1a;折纸折几次整颗二叉树就有…

Jmeter的接口自动化测试

在去年实施了一年的三端&#xff08;PC、无线M站、无线APP【Android、IOS】&#xff09;后&#xff0c;今年7月份开始&#xff0c;我们开始进行接口自动化的实施&#xff0c;目前已完成了整个框架的搭建以及接口的持续测试集成。今天做个简单的分享。 在开始自动化投入前&#…

独家揭秘微信视频号下载提取器,使用方法!

1&#xff1a;微信视频号下载提取器&#xff0c;需要先确认自己手机电脑版本是否支持视频号的观看和浏览 2:需要下载视频号的作品发给视频下载小助手&#xff0c;聊天窗口 3&#xff1a;打开小助手解析视频号视频链接&#xff0c;保存到手机相册或者电脑上 注意视频号电脑版…

还原现场——前端录制用户行为技术方案

一、问题背景 目前&#xff0c;在我们的项目中通常会使用各种各样的埋点和监控来收集页面访问的信息&#xff0c;例如点击埋点、PV埋点等&#xff0c;这些埋点数据能够反应绝大部分的用户行为&#xff0c;但是对于一些关注上下文的使用场景而言这些埋点是不够的。 对于产品而言…

智慧工地云平台源码 人工智能AI+多系统集成+智能预警平台源码

智慧工地云平台源码 人工智能AI多系统集成智能预警平台 智慧工地企业级监管平台融入AIoT、移动互联网和物联网等领先技术&#xff0c;再结合工地“人、机、料、法、环”五大要素&#xff0c;劳务实名制管理、环境监测管理、安全施工管理、质量及能耗管理等智慧化应用&#xff0…

华为云香港云服务器CPU性能测评_1核2G1M带宽S3服务器

华为云香港云服务器99元一年&#xff0c;S3云服务器1核2G1M带宽&#xff0c;40GB高IO系统盘&#xff0c;活动链接&#xff1a;atengyun.com/go/huawei 购买条件为华为云新用户&#xff0c;阿腾云分享华为云香港99元服务器优惠活动入口、详细配置、价格及注意事项&#xff1a; …

【LeetCode:2698. 求一个整数的惩罚数 | 递归】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

Mac 超好用的工具推荐

Arc Arc 是 2022 年 4 月发布的浏览器产品&#xff0c;在介绍 Arc 浏览器之前&#xff0c;让我们来看下以 Chrome、FireFox、Edge、Safari 为代表的的传统浏览器&#xff1a; 难怪《浏览器是怎么工作的》作者 Paul Irish 曾说&#xff0c;尽管 W3C 并未规范浏览器界面&#xf…

Python深度学习实战-基于class类搭建BP神经网络实现分类任务(附源码和实现效果)

实现功能 上篇文章介绍了用Squential搭建BP神经网络&#xff0c;Squential可以搭建出上层输出就是下层输入的顺序神经网络结构&#xff0c;无法搭出一些带有跳连的非顺序网络结构&#xff0c;这个时候我们可以选择类class搭建封装神经网络结构。 第一步&#xff1a;import ten…

手机扫描二维码的测试用例

二维码概述 二维码本身就是一个URL&#xff0c;只是通过QR码的形式把URL和用户身份信息转换成二进制的0和1&#xff0c;二维码中黑色的色素块代表1&#xff0c;白色的色素块代表0&#xff0c;我们通过相机扫码&#xff0c;就获取了二维码中的URL。 ****测试用例罗列&#xff08…

通过IP地址可以做什么

通过IP地址可以做很多事情&#xff0c;因为它是互联网通信的基础之一。本文将探讨IP地址的定义、用途以及一些可能的应用。 IP地址的用途 1. 设备标识&#xff1a;IP地址用于标识互联网上的每个设备&#xff0c;这包括计算机、服务器、路由器、智能手机等。它类似于我们日常生…

美摄AR人像美颜,全新视觉体验

企业越来越重视通过视觉媒体来提升品牌形象和吸引客户。然而&#xff0c;传统的摄影技术往往无法满足企业对于高质量、个性化视觉内容的需求。这时&#xff0c;美摄AR人像美颜解决方案应运而生&#xff0c;它以其独特的技术和优势&#xff0c;为企业带来了全新的视觉体验。 美…

解读意大利葡萄酒分类系统

由于该国众多的产区和复杂的品种&#xff0c;要想真正掌握意大利葡萄酒是相当困难的。仅仅是试图从复杂混乱的葡萄酒标签中辨别信息的想法就足以让许多人焦虑不安。 位于托斯卡纳的基安蒂酒地区&#xff0c;Il Ciliegio生产的葡萄酒标签上包含以下名称之一:基安蒂酒科利塞内西…

2-Java进阶知识总结-3-集合

文章目录 补充知识-泛型版本信息泛型类常见泛型标识符泛型应用实例注意事项 泛型方法非静态的泛型方法静态的泛型方法&#xff1a;必须声明出自己独立的泛型 泛型接口泛型通配符 补充知识-树基本概念二叉树-普通二叉树二叉树-二叉查找树添加节点流程优点、不足 二叉树-平衡二叉…

了解松散类型

目录 松散类型带来的优势 灵活性和便利性 快速原型开发 动态类型 松散类型的注意事项 类型转换 隐式类型转换 如何正确使用松散类型&#xff1f; 动态类型 便捷的类型转换 灵活性与易用性 潜在的隐式类型转换 避免混淆和错误 当谈到JavaScript编程语言时&#xff…

app拉新平台推广渠道,地推网推接单神器

地推或网推不知道在哪里接单&#xff1f;信息差太大导致价格参差不齐&#xff0c;聚量推客人人高价置顶 聚量推客自己本身是服务商直营平台 相对来说数据更好&#xff0c;我们也拿到了平台首码&#xff1a;000000 填这个就行&#xff0c;属于官方渠道 平台最新上架的产品有&a…