Harmony实现流转开发之音乐播放器跨设备流转 - 实践

news/2025/9/29 18:24:02/文章来源:https://www.cnblogs.com/lxjshuju/p/19119291

目录:

    • 一、场景定义
    • 二、技术方案
    • 三、完整代码实现(基于 ArkTS)
    • 四、运行流程演示
    • 五、关键技术点总结
    • 六、进阶优化

一、场景定义

  • 设备A(手机):正在播放音乐(歌曲《Hello World》,进度 1:20),用户点击“流转”按钮。
  • 设备B(平板):被手机发现并选中,接收音乐状态(歌曲ID、播放进度、音量等),自动启动音乐应用并从 1:20 续播。
  • 核心目标:实现“断点续播”和“无缝体验”,用户无感知设备切换。

二、技术方案

三、完整代码实现(基于 ArkTS)

Step 1:配置权限(module.json5)
在应用配置文件中声明分布式能力权限:

{
"module": {
"reqPermissions": [
{ "name": "ohos.permission.DISTRIBUTED_DEVICE_STATE_CHANGE" }, // 设备状态权限
{ "name": "ohos.permission.GET_DISTRIBUTED_DEVICE_INFO" },    // 获取设备信息
{ "name": "ohos.permission.DISTRIBUTED_DATASYNC" },           // 数据同步权限
{ "name": "ohos.permission.INTERACT_ACROSS_DEVICES" },        // 跨设备交互
{ "name": "ohos.permission.READ_MEDIA" }                      // 读取媒体文件(播放音乐)
],
"distributedNotificationEnabled": true, // 允许分布式通知
"abilities": [
{
"name": "MusicAbility", // 音乐播放 FA 组件(可流转的页面)
"type": "page",
"visible": true
}
]
}
}

Step 2:设备发现组件(DeviceDiscovery.ets)
实现“发现周围鸿蒙设备”的 UI 组件,供用户选择流转目标:

import deviceManager from '@ohos.distributedDeviceManager';
import promptAction from '@ohos.promptAction';
@Preview
@Component
export default struct DeviceDiscovery {
private dmInstance: deviceManager.DeviceManager | null = null;
private deviceList: deviceManager.DeviceInfo[] = []; // 发现的设备列表
private selectedDeviceId: string = ''; // 用户选中的设备 ID
// 组件初始化时创建设备管理器
aboutToAppear() {
this.initDeviceManager();
}
// 创建设备管理器实例,监听设备变化
private initDeviceManager() {
try {
deviceManager.createDeviceManager('com.example.musicplayer', (err, manager) => {
if (err) {
promptAction.showToast({ message: `设备管理初始化失败: ${err.message}` });
return;
}
this.dmInstance = manager;
// 订阅设备状态变化(设备上线/下线)
this.dmInstance.on('deviceStateChange', (device: deviceManager.DeviceInfo) => {
if (device.status === 0) { // 0: 设备在线
this.deviceList.push(device); // 添加到设备列表
} else { // 设备离线
this.deviceList = this.deviceList.filter(d => d.deviceId !== device.deviceId);
}
});
// 主动扫描周围设备
this.dmInstance.discoverDevices();
});
} catch (error) {
console.error('初始化设备管理器失败:', error);
}
}
// 用户选择设备后触发流转
private async onSelectDevice(deviceId: string) {
this.selectedDeviceId = deviceId;
// 调用音乐流转逻辑(下一步实现)
await this.transferMusicToDevice(deviceId);
}
// 音乐流转核心逻辑(后文实现)
private async transferMusicToDevice(targetDeviceId: string): Promise<void> {// ... 见 Step 3}build() {Column() {Text('选择流转设备').fontSize(18).margin(10);// 展示发现的设备列表List() {ForEach(this.deviceList, (device: deviceManager.DeviceInfo) => {ListItem() {Text(`${device.deviceName} (${device.deviceType})`) // 设备名+类型(手机/平板).fontSize(16).width('100%').padding(10).onClick(() => this.onSelectDevice(device.deviceId));}});}.height(200).width('100%');}.padding(20);}}

Step 3:音乐流转逻辑(音乐续播核心)
在 transferMusicToDevice 方法中,实现“获取当前播放状态 → 传递状态到目标设备 → 启动目标设备音乐应用”:

import featureAbility from '@ohos.ability.featureAbility';
import Want from '@ohos.app.ability.Want';
import media from '@ohos.multimedia.media'; // 音乐播放器 API
// 全局音乐播放器实例(简化示例)
let player: media.AVPlayer = media.createAVPlayer();
// 音乐流转核心函数
private async transferMusicToDevice(targetDeviceId: string) {
try {
// 1. 获取当前播放状态(歌曲ID、进度、音量等)
const currentState = await this.getPlaybackState();
// 2. 构造 Want 对象(指定目标设备和要启动的 FA 组件)
const want: Want = {
deviceId: targetDeviceId, // 目标设备 ID(从设备列表选择)
bundleName: 'com.example.musicplayer', // 当前应用包名
abilityName: 'MusicAbility', // 音乐播放 FA 组件名
parameters: {
// 传递播放状态(需序列化,使用 JSON.stringify)
musicState: JSON.stringify({
songId: currentState.songId, // 歌曲唯一 ID
progress: currentState.progress, // 当前播放进度(秒)
volume: currentState.volume, // 音量(0-100)
isPlaying: currentState.isPlaying // 是否正在播放
})
}
};
// 3. 跨设备启动 MusicAbility(触发应用流转)
await featureAbility.startAbility({ want: want });
// 4. 本地暂停播放(可选:流转后当前设备停止播放)
await player.pause();
promptAction.showToast({ message: `已流转到 ${this.getDeviceName(targetDeviceId)}` });
} catch (error) {
console.error('音乐流转失败:', error);
promptAction.showToast({ message: '流转失败,请重试' });
}
}
// 获取当前播放状态(从播放器实例读取)
private async getPlaybackState(): Promise<{
songId: string,
progress: number,
volume: number,
isPlaying: boolean
}> {
return {
songId: 'song_001', // 示例:当前播放歌曲 ID
progress: await player.getCurrentTime(), // 获取当前播放进度(秒)
volume: player.getVolume(), // 获取当前音量
isPlaying: player.state === media.PlaybackState.PLAYING // 是否播放中
};
}
// 根据设备 ID 获取设备名称(简化:从设备列表匹配)
private getDeviceName(deviceId: string): string {
const device = this.deviceList.find(d => d.deviceId === deviceId);
return device?.deviceName || '未知设备';
}

Step 4:目标设备恢复播放(MusicAbility.ets)
在目标设备(平板)的 MusicAbility 中,接收流转状态并恢复播放:

import Ability from '@ohos.app.ability.Ability';
import Want from '@ohos.app.ability.Want';
import media from '@ohos.multimedia.media';
export default class MusicAbility extends Ability {
private player: media.AVPlayer = media.createAVPlayer(); // 目标设备播放器
async onCreate(want: Want) {
super.onCreate(want);
// 判断是否为跨设备流转启动(通过 want.parameters 检测)
if (want?.parameters?.musicState) {
await this.restoreMusicState(want.parameters.musicState as string);
}
}
// 恢复音乐播放状态
private async restoreMusicState(musicStateStr: string) {
try {
// 1. 解析流转传递的状态
const musicState = JSON.parse(musicStateStr);
const { songId, progress, volume, isPlaying } = musicState;
// 2. 设置播放器参数(音量、歌曲源)
this.player.setVolume(volume / 100); // 音量归一化(0-1)
await this.player.reset();
await this.player.setSource({ uri: `dataability:///media/songs/${songId}` }); // 歌曲 URI(示例)
await this.player.prepare();
// 3. 跳转到指定进度并播放
await this.player.seek(progress * 1000); // seek 单位为毫秒
if (isPlaying) {
await this.player.play(); // 续播
}
console.log(`音乐流转恢复成功:歌曲 ${songId},进度 ${progress}`);
} catch (error) {
console.error('恢复播放状态失败:', error);
}
}
}

四、运行流程演示

1、手机端操作:

  • 播放歌曲《Hello World》,进度 1:20。
  • 点击“流转”按钮,触发 DeviceDiscovery 组件发现平板设备。
  • 选择平板后,调用 transferMusicToDevice 传递状态并暂停本地播放。

2、平板端结果:

  • 自动启动音乐应用,MusicAbility 的 onCreate 接收 musicState。
  • 解析状态后,播放器跳转到 1:20 并续播,音量与手机端一致。

五、关键技术点总结

在这里插入图片描述

六、进阶优化

  1. 分布式 KV 存储同步播放进度:
    若音乐进度实时变化(如用户在平板手动调整进度),可通过 分布式 KV 存储 双向同步进度,避免依赖单次Want 传递。

  2. 设备类型适配:
    平板屏幕较大,可在 MusicAbility 中根据 deviceType 调整 UI(如歌词显示区域更大)。

  3. 异常处理:
    目标设备无该歌曲时,显示“歌曲未同步”提示,引导用户通过分布式文件服务拉取歌曲文件。

总结:
通过 设备发现 → 状态传递 → 跨设备启动 → 状态恢复 四步,实现了音乐播放器的跨设备流转。核心是利用鸿蒙的 DeviceManager(设备管理)和 Want(跨设备通信)能力,结合应用状态序列化传递,实现“无缝断点续播”。这一模式可扩展到视频、文档、游戏等更多流转场景。

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

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

相关文章

软件工程中线性回归应用

软件工程中线性回归应用引言:连接统计学与软件开发在现代软件工程领域,行业范式正在经历一场深刻的变革,决策过程正从依赖直觉和经验转向基于数据的严谨分析 。从开发生命周期中收集和分析数据,已不再是少数前沿团…

解决秒杀高并发的一些方案

解决秒杀高并发的一些方案 秒杀场景的特点:瞬时高并发:大量请求在短时间内涌入 库存有限:必须避免超卖 一致性要求高:库存扣减和订单生成要保证正确常见的几种实现方式:一、Redis 方案(基于内存,效率高) 1. 库…

多级缓存(Memory+Regdis)

多级缓存(Memory+Regdis)根据WebAPI更改,请参考上一章节 在大并发模式下,我们可以除了数据库存,再加上本地的内存缓存查询,或分布式查询 把内存+分存式查询放在一起,叫做多级缓存。 1. 内存查询先在NuGet中引用Mi…

构建移动网关:Air780EPM用4G为WiFi和LAN设备供网

利用Air780EPM开发板,可构建一个便携式移动网关,以4G网络为出口,为WiFi和以太网设备提供即插即用的互联网接入服务。 一、多网融合概述 Air780EPM 开发板通过多网融合技术将不同类型的通信网络(4G、以太网)整合在…

9.29模拟赛总结

赛前 刚哥说要以正式考试的心态打模拟赛 所以提前设了一个目标,230+和rk<=5 然后和大赛前一样,先调整状态,深呼吸什么的 赛时 开始时还剩3:45时间 T1上来分析了一下性质 在3:24时秒了 T2看见直径,然后直接想端…

优化 if/else 的四种设计模式

在日常开发中,我们经常会遇到需要根据不同条件执行不同逻辑的场景,导致代码中出现大量的if/else嵌套。这不仅降低了代码的可读性和可维护性,还会增加后续扩展的难度。 本文将介绍四种优雅的设计模式来优化这种"…

多corner综合

综合时指定多个cornet的工艺库进行分析一般一个工艺会提供不同工艺角的标准单元库,如果希望在综合的时候能在两个边界工艺库上做分析,可以利用set_min_library和set_operating_conditions两个命令。 set_min_library…

Day11-C:\Users\Lenovo\Desktop\note\code\JavaSE\Basic\src\com\oop\demo06

多态 动态编译 指的是,同一方法可以根据发送对象的不同而采取多种不同的行为方式 instanceof (类型转换)引用类型,判断一个对象是什么类型 /* 多态注意事项多态是方法的多态,属性没有多态 父类和子类,有联系 类…

注册网站域名需要什么网站更换图片之类的怎么做

在 VSCodium 中安装仓颉编程语言的插件 VSCodium是基于微软编辑器 VS Code源代码编译制作&#xff0c;去掉了微软的遥测功能&#xff0c;是一个由社区驱动、自由许可的二进制发行版。 仓颉编程语言的开发插件是一个需要从本地安装的插件。其官方网站上&#xff0c;写出了其安装…

牛客周赛 Round 111

https://ac.nowcoder.com/acm/contest/117763 E 在此题中,我们认为数组以从左到右的顺序排列。 对于一个数组 \(a\),小芳定义两个函数 \(L\left( a\right)\) 与 \(R\left( a\right)\) 为: \(\hspace{23pt} \bullet\…

高端母婴网站模板网络营销渠道

云原生专栏大纲 文章目录 准备工作项目结构介绍配置安全测试ConfigMapSecret使用Secret中数据的方式Deployment使用Secret配置Secret加密 kustomize部署清单ConfigMap改造SecretSealedSecretDeployment改造Serviceistio相关资源DestinationRuleGatewayVirtualServiceServiceAc…

OpenLayers地图交互 -- 章节十一:拖拽材料交互详解

OpenLayers地图交互 -- 章节十一:拖拽材料交互详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas"…

2025年人工智能与智能装备国际学术会议(AIIE 2025)

2025年人工智能与智能装备国际学术会议(AIIE 2025) 2025 International Conference on Artificial Intelligence and Intelligent Equipment 在这里看会议官网详情 时间:2025年11月7日-9日 地点:中国西安 本轮截稿…

详细介绍:衡石HQL深度解析:如何用类SQL语法实现跨源数据的高效联邦查询?

详细介绍:衡石HQL深度解析:如何用类SQL语法实现跨源数据的高效联邦查询?2025-09-29 18:00 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x:…

通过IDOR实现权限提升导致未授权用户注入

本文详细介绍了作者在公开漏洞赏金项目中发现的IDOR漏洞,通过修改contactId参数实现权限提升,能够将任意用户添加到设备管理组中,获得了相应的漏洞赏金。通过IDOR实现权限提升导致未授权用户注入 再次问候,我是Oma…

APUE学习笔记之基础知识(一) - Invinc

本文记录《UNIX环境高级编程》第3版中第1章 基础知识 的一些知识点。 包括UNIX体系结构和登录、文件和目录、输入和输出、程序和进程、出错处理、用户标识、信号、时间值、系统调用和库函数等基本概念。本文记录《UNIX…

Syslog日志集成搭建

搭建一个完整的 syslog → Filebeat → Elasticsearch → Kibana 测试环境,详细的分步骤配置手册,包括 Linux (Filebeat) 和 Windows (Elasticsearch + Kibana) 的安装和配置。 部署架构 Filebeat:运行在 Linux 服务…

拿别的公司名字做网站工业互联网平台首先要提高数据的挖掘能力

当string&#xff0c;number函数不被用作构造函数的可以当成转换函数 如 string(false),number(‘3’),boolean([]) Object(3) new number(3); 除了NULL和undefined以外任何值都具有toString()方法 JS 在执行程序的时候会自动检测表达式来进行变量转换。 显示转换变量&#xf…

定义工业生产新范式!网易灵动发布全球首款全域智能无人装载机“灵载”

近日,在全球最具影响力的工程机械盛会——BICES 2025现场,网易旗下工程机械智能化品牌网易灵动隆重召开无人装载机智能解决方案发布会,正式推出全球首款面向全域场景的具身智能无人装载机——“灵载”。 此次发布标…

国有银行人力资源数字化转型的合规突围与效能跃迁

在数字化浪潮下,金融行业正面临深刻变革。作为数字中国建设的重要支撑,国家金融数字化战略明确要求银行体系充分应用数字技术,优化服务模式、提升管理效能。国有银行作为金融体系的中流砥柱,不仅需要驱动外部服务数…