鸿蒙新特性 | 跨设备流转——手机拍的视频平板接着播
一、我们想解决什么问题生活里的三个卡壳瞬间先不说技术聊几个你一定经历过的场景。场景一视频接力。你在地铁上用手机刷完了半小时的视频精华片段回家后想舒舒服服用平板的大屏幕继续看同一个视频的后续内容。这时候你要怎么做大概率是打开平板 → 打开视频 App → 登录账号 → 搜索视频 → 拖进度条。步骤不少而且如果 App 不支持登录多设备还要忍受奇怪的提示。场景二文档接力。你在手机上随手拍了一张会议白板的照片回到工位想用电脑上的修图工具处理一下。以前的选择是微信文件传输助手、AirDrop、或者数据线直连。每一种都有点麻烦尤其是当你身边没有 WiFi 的时候。场景三导航接力。你在车上用手机导航快到家了但导航还在继续下车后想把导航甩到手表上继续步行指引。想象一下这个画面——你拎着东西站在小区门口手机屏幕亮了半天定位飘来飘去结果还得重新输入目的地。这三个场景的共同点是任务在一台设备上开始但用户想在另一台设备上结束。没有跨设备流转你就得手动交接有了跨设备流转系统自动帮你把上下文传递过去对用户来说感觉就像——视频从来没有停过。技术上的卡点在哪里好有了需求我们来看看技术上卡在哪里。第一设备发现。你要把视频传给平板首先得知道平板在哪。手机周围可能有电视、耳机、音箱、平板……系统要能认出谁是你的自己人而不是邻居的设备。这需要一个可靠的设备发现机制。第二数据传递。视频文件动不动几百 MB直接走蓝牙太慢走云端太贵最好能走局域网直连。更重要的是传递的不只是文件本身——还有播放到第几分钟第几秒这样的状态信息。第三一致性。两台设备看到的内容要一致手机上暂停了平板上也得暂停手机上切到了某个进度平板上打开也要从同一个位置开始。第四权限与安全。不是所有设备都值得信任也不是所有数据都应该被分享。HarmonyOS 通过同一华为账号 同一网络的双重校验来确保安全。理解了这四个卡点后面的设计你就明白为什么这么做了。二、数据模型设计先建模再写代码技术方案的第一步永远是先把数据模型定义清楚。就像盖房子之前先画结构图先把有什么东西东西长什么样说清楚后面的代码才有根可循。我们不需要搞什么复杂的类继承TypeScript 的 interface 就是干这个的——清晰、直观、够用。设备信息DeviceInfo想象你走进一个会议室里面坐了好几个人你要先认识他们才能决定跟谁合作。DeviceInfo 就是设备的身份证// 设备信息接口interfaceDeviceInfo{deviceId:string;// 设备唯一标识UUID 格式deviceName:string;// 用户给设备起的名字如小明的小米平板deviceType:DeviceType;// 设备类型手机/平板/PC/手表等networkType:NetworkType;// 网络类型WiFi / 5G / 离线isTrusted:boolean;// 是否为可信设备同账号且已授权batteryLevel:number;// 当前电量0~100}// 设备类型枚举enumDeviceType{PHONEphone,TABLETtablet,PCpc,WATCHwatch,TVtv,UNKNOWNunknown}// 网络类型枚举enumNetworkType{WIFIwifi,CELLULARcellular,DISCONNECTEDdisconnected}流转会话TransferSession设备认识完了接下来要建立一条任务通道。就像你和同事之间建一个工作群群里定义好这个任务从哪来、传给谁、传什么内容、进度如何。// 流转会话接口interfaceTransferSession{sessionId:string;// 会话唯一 IDsourceDevice:DeviceInfo;// 发送端设备targetDevice:DeviceInfo;// 接收端设备contentType:ContentType;// 内容类型视频/图片/文档/链接contentUri:string;// 内容 URI可以是本地路径或分布式路径contentMeta:ContentMeta;// 内容元数据status:TransferStatus;// 当前状态准备/传输中/完成/失败progress:number;// 传输进度0~100createdAt:number;// 创建时间戳expiresAt:number;// 会话过期时间避免资源泄漏}// 内容元数据interfaceContentMeta{title:string;// 内容标题duration?:number;// 视频/音频时长秒position?:number;// 播放位置秒用于视频续播fileSize?:number;// 文件大小字节mimeType:string;// MIME 类型}// 传输状态枚举enumTransferStatus{IDLEidle,PREPARINGpreparing,TRANSFERRINGtransferring,COMPLETEDcompleted,FAILEDfailed,CANCELLEDcancelled}内容类型ContentType流转的内容五花八门笼统传一个大文件包不够细致我们需要细分// 内容类型枚举enumContentType{VIDEOvideo,// 视频文件IMAGEimage,// 图片文件DOCUMENTdocument,// 文档PDF/Word/Excel 等URLurl,// 链接网页/App 页面AUDIOaudio,// 音频文件LOCATIONlocation,// 地理位置用于导航接力}小结模型设计的思路你可能发现了这三个接口层层递进DeviceInfo解决设备是谁的问题TransferSession解决任务怎么跑的问题ContentType解决传的是什么的问题。先把架子搭好后面的代码就像填空题一样简单。三、核心设计决策方案对比为什么我们这样设计技术选型不是最先进最好而是最适合当前场景。我们来看几个关键决策点。决策一传输协议选 SoftBus 还是 HTTP对比维度SoftBus分布式软总线HTTP 轮询/推送传输速度内网极快百兆文件秒传依赖服务器较慢离线支持同一华为账号可离线传递必须有外网开发成本HarmonyOS 原生封装开箱即用需要自建服务适用场景大文件视频/图片小数据状态同步我们的结论是以 SoftBus 为主HTTP 作为兜底。大文件走软总线享受极致速度小状态走轻量协议保证可用性。决策二状态存储用本地还是分布式数据服务跨设备流转最怕的就是手机停了、平板不知道从哪开始。HarmonyOS 提供了分布式数据服务Distributed Data Service能让多台设备共享同一份数据而且自动同步。// 选型对比constisLocalOnlyfalse;// ❌ 只存本地另一台设备拿不到状态constisCloudSynctrue;// ⚠️ 云同步慢依赖网络还收费constisDistributedDBtrue;// ✅ 分布式数据库本地化速度跨设备同步决策三发现设备用蓝牙还是 WiFi蓝牙功耗低但速度慢适合配对阶段WiFi 速度快适合大数据传输。HarmonyOS 的做法是先用蓝牙低功耗广播发现设备再用 WiFi Direct 建立高速通道传输数据。扬长避短效率最高。决策四用户体验上推送制还是拉取制推送制手机主动推给平板手机控制感强但平板可能不想收比如正在打游戏。拉取制平板主动从手机拉数据平板自主可控但需要平板主动发起。我们的选择用户确认 智能推荐。检测到用户在两台设备上都有同一个 App 时弹出是否继续在平板上观看的提示让用户自己决定。四、完整代码实现代码文件一设备发现服务 DeviceDiscovery.ets这是整个流转的入口。我们用 HarmonyOS 提供的分布式能力先找到附近的设备// DeviceDiscovery.ets - 设备发现服务importdeviceManagerfromohos.distributedHardware.deviceManager;classDeviceDiscovery{privatedmInstance:deviceManager.DeviceManager|nullnull;privatedeviceList:DeviceInfo[][];// 初始化设备管理器initialize(){deviceManager.createDeviceManager(com.atomgit.app,(err,dm){if(err){console.error(设备管理器创建失败:,err);return;}this.dmInstancedm;this.startDiscovery();});}// 开始发现可信设备startDiscovery(){if(!this.dmInstance)return;// 获取同一华为账号下的可信设备列表consttrustedDevicesthis.dmInstance.getTrustedDeviceListSync();this.deviceListtrustedDevices.map((device:any)({deviceId:device.deviceId,deviceName:device.deviceName,deviceType:this.mapDeviceType(device.deviceType),isTrusted:true,networkType:NetworkType.WIFI}));console.info(发现可信设备数量:,this.deviceList.length);}// 获取可用设备列表过滤掉自己getAvailableDevices():DeviceInfo[]{constmyDeviceIdthis.dmInstance?.getLocalDeviceInfoSync()?.deviceId;returnthis.deviceList.filter(dd.deviceId!myDeviceId);}privatemapDeviceType(type:number):DeviceType{constmap:Recordnumber,DeviceType{0x00:DeviceType.PHONE,0x01:DeviceType.TABLET,0x02:DeviceType.PC,0x03:DeviceType.WATCH,0x04:DeviceType.TV,};returnmap[type]||DeviceType.UNKNOWN;}}代码文件二流转服务 TransferService.ets设备找到了接下来就是建一条流转通道把内容送过去// TransferService.ets - 跨设备流转服务importdistributedChannelfromohos.distributedHardware.channel;importfileIOfromohos.fileio;classTransferService{privatesession:distributedChannel.DistributedChannel|nullnull;// 创建流转会话asynccreateSession(source:DeviceInfo,target:DeviceInfo,content:ContentMeta):PromiseTransferSession{constsessionIdthis.generateUUID();// 建立跨设备通信通道走 SoftBusthis.sessionawaitdistributedChannel.createSession({sessionName:transfer_${sessionId},peerDeviceId:target.deviceId,dataType:distributedChannel.DataType.FILE,});return{sessionId,sourceDevice:source,targetDevice:target,contentType:ContentType.VIDEO,contentUri:,contentMeta:content,status:TransferStatus.PREPARING,progress:0,createdAt:Date.now(),expiresAt:Date.now()30*60*1000// 30 分钟后过期};}// 传输文件带进度回调asynctransferFile(session:TransferSession,filePath:string,onProgress:(pct:number)void):Promisevoid{if(!this.session)thrownewError(会话未建立);session.statusTransferStatus.TRANSFERRING;// 打开本地文件通过分布式通道发送constfdfileIO.open(filePath,fileIO.OpenMode.READ_ONLY);constfileSizefileIO.statSync(filePath).size;letsent0;constbuffernewArrayBuffer(8192);letbytesRead0;// 分块传输每传完一块更新进度while((bytesReadfileIO.read(fd,buffer))0){awaitthis.session.write(buffer.slice(0,bytesRead));sentbytesRead;constpctMath.floor((sent/fileSize)*100);onProgress(pct);session.progresspct;}fileIO.close(fd);session.statusTransferStatus.COMPLETED;console.info(文件传输完成sessionId:,session.sessionId);}privategenerateUUID():string{returnxxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx.replace(/[xy]/g,c{constrMath.random()*16|0;return(cx?r:(r0x3|0x8)).toString(16);});}}代码文件三续播接力 VideoContinuity.ets传完文件还不够关键是从哪继续看。这段代码管理播放位置的记录和恢复// VideoContinuity.ets - 视频续播接力importdistributedKVfromohos.distributedKvStore;classVideoContinuity{privatekvStore:distributedKV.KVStore|nullnull;privatereadonlyKV_IDvideo_position_db;// 初始化分布式键值数据库asyncinit(){constoptions:distributedKV.Options{createIfMissing:true,kvStoreType:distributedKV.KVStoreType.SINGLEVERSION};this.kvStoreawaitdistributedKV.createKVStore(this.KV_ID,options);}// 记录当前播放位置每次播放进度变化时调用asyncsavePosition(videoId:string,position:number,deviceId:string){if(!this.kvStore)awaitthis.init();constrecord{videoId,position,// 秒为单位deviceId,// 记录是哪台设备保存的timestamp:Date.now()};awaitthis.kvStore.put(pos_${videoId},JSON.stringify(record));console.info(保存播放位置:${videoId}-${position}s);}// 获取续播位置打开视频时调用asyncgetResumePosition(videoId:string):Promisenumber|null{if(!this.kvStore)awaitthis.init();constrawawaitthis.kvStore.get(pos_${videoId});if(!raw)returnnull;constrecordJSON.parse(raw);// 只接受 24 小时内的记录避免返回过时位置constmaxAge24*60*60*1000;if(Date.now()-record.timestampmaxAge)returnnull;returnrecord.position;}// 清除记录用户主动从头播放时调用asyncclearPosition(videoId:string){if(!this.kvStore)return;awaitthis.kvStore.delete(pos_${videoId});}}小结三个文件的职责划分设备发现找到谁是我的小伙伴流转服务负责怎么把东西送过去续播接力管理到了新设备从哪接上。三个模块各司其职通过统一的 Session 数据结构串联起来。五、深度技术原理软总线设备之间的隐形高速公路好理解了代码之后我们来聊聊背后的原理。想象一下你的手机和平板之间没有一根数据线也没有云端服务器做中转那它们是怎么对话的答案就是HarmonyOS 的分布式软总线SoftBus。打个比方。普通的设备互联就像这样你从深圳寄一个包裹到广州先把包裹送到北京的总仓库北京、上海、深圳三个仓库共用一个大系统总仓库再转发到广州。绕了一大圈速度慢还贵。软总线不一样它更像是在深圳和广州之间建了一条隐形直达隧道。两座城市之间直接拉一根光纤不需要绕到北京速度快了好几倍。这条隧道可以走 WiFi可以走 USB甚至未来可以走蓝牙——但对开发者来说这些细节完全被屏蔽了你只需要发送和接收就像读写本地文件一样简单。超级终端信任关系的建立你可能会问万一邻居的手机偷偷往我平板上发东西怎么办这就涉及到超级终端的信任体系了。HarmonyOS 的设备信任关系建立在华为账号上。只有满足以下两个条件的设备才能互相发现和通信同一华为账号登录。手机和平板登录的是同一个华为账号系统认定这两台设备属于同一个人。同一局域网或近距离。设备需要在同一个 WiFi 网络下或者通过 HarmonyOS 的近场感知能力建立连接。只有同时满足这两个条件设备才会出现在可用设备列表里你的视频才不会发错地方。续播的原理状态是如何跨设备保持的你可能还有个疑问手机上的播放位置平板是怎么知道的两台设备又没有共享内存。这就靠Distributed KV分布式键值数据库了。你可以把它理解为一个共享笔记本。手机在播放视频时每隔几秒就在这本笔记本上写一行“《流浪地球》第 42 分钟 37 秒”平板打开同一个 App 时去这本笔记本上一查——哦原来上次停在这里那我就从第 42 分 37 秒开始播。这本共享笔记本在本地有一份副本数据变化时会自动同步到同账号的所有设备上速度很快而且不需要云端服务器中转数据走的就是前面说的那条隐形直达隧道。所以即便你坐飞机没有网络只要手机和平板之前同步过一次位置最后一次有网的时候续播依然可以工作。设备发现的两阶段策略前面提到 HarmonyOS 用蓝牙发现 WiFi 传输的组合策略这里展开说说第一阶段发现。设备通过蓝牙低功耗BLE广播自己的存在就像在喊我在这里我叫小明的小米平板“。手机收到广播后根据华为账号信息判断是不是自己人”如果是就记录下来。这个过程功耗极低手机搜半天也不怎么费电。第二阶段传输。确认目标设备可信后切换到 WiFi Direct 或者同一个局域网建立高速连接。这时候传输速度就上来了1GB 的视频文件在千兆局域网下几十秒就能传完。BLE 负责认识你WiFi 负责给你搬东西两个协议各司其职。这是通信领域很常见的控制面 数据面分离思想HarmonyOS 只是把它用在了分布式设备场景。分布式键值数据库的工作机制Distributed KV 之所以能做到不需要云端服务器关键在于它的本地优先 增量同步机制。每台设备的本地都有一份完整的数据副本数据写入时先落本地数据库然后通过 SoftBus 将增量变更只同步变化的部分而不是整个数据库推送给同账号设备。推送使用基于版本的乐观锁——如果两台设备同时修改了同一份数据版本号更高的那次修改优先冲突概率极低因为播放位置这类数据天然不存在并发写入。为什么不用传统的数据库同步想象一下手机在地铁里没有网络此时平板也离线两台设备都独立播放了同一个视频各自记住了不同的位置。网络恢复后谁的位置是正确的分布式 KV 用最后写入胜出Last Write Wins策略配合时间戳来解决这个问题简单高效适合大多数消费级场景。流转会话的生命周期管理一个完整的流转会话从创建到销毁经历了五个状态① 准备Preparing源设备找到目标设备建立通信通道协商传输参数。这个阶段用户感知到的是正在发现设备。② 传输Transferring文件或状态数据开始搬运。对于小文件几百 KB 以内这个阶段几乎瞬间完成对于大视频文件则会显示传输进度条。③ 确认Confirming文件到达目标设备后目标 App 被唤起并验证数据完整性比如 MD5 校验。确认无误后进入完成状态。④ 完成Completed流转成功两台设备上的 App 均显示当前状态Session 进入已过期倒计时默认 30 分钟防止资源泄漏。⑤ 失败/取消Failed/Cancelled网络中断、目标设备拒绝接收、磁盘空间不足等情况都会触发失败状态。此时源设备会提示用户原因目标设备不会启动任何 App。理解这五个状态对于调试流转问题非常重要——当你发现流转卡住了看一下 Session 日志里卡在哪一步排查方向就清晰多了。六、常见问题解答Q1跨设备流转支持所有 App 吗不是的。跨设备流转需要在应用层主动接入 HarmonyOS 的分布式 SDK。目前主流的华为自带应用华为视频、华为音乐、华为浏览器等已经内置支持。如果你用的是第三方 App目前还无法自动享受流转体验——这也是 HarmonyOS 正在推动生态建设的方向之一。Q2传输过程中设备断开网络会怎样分两种情况。如果传输的是大文件视频、图片SoftBus 传输中途断开网络恢复后会自动续传不需要从头开始。如果传输的是小状态数据播放位置、书签它们已经存储在分布式 KV 数据库中断网不影响读取——因为数据在本地有缓存。Q3设备之间需要连到同一个 WiFi 吗不完全是。同一华为账号的设备即便不在同一个局域网内也可以通过 HarmonyOS 的分布式能力进行数据同步延迟稍高。但如果要进行大文件的高速传输还是建议在同一 WiFi 网络下速度会快很多。当然如果你用的是 HarmonyOS 3.0 及以上版本手机和平板在近距离时还能通过HarmonyOS 超级终端一碰连建立直连通道完全不需要 WiFi。Q4哪些设备支持跨设备流转理论上所有 HarmonyOS 2.0 及以上版本的设备都支持基础的设备发现能力。但完整的流转体验如视频续播接力需要设备同时满足搭载 HarmonyOS 2.0、登录同一华为账号、打开跨设备协同开关。目前手机、平板、PC、手表、智慧屏都支持具体能力因设备性能有所差异。Q5我的隐私安全有保障吗有。HarmonyOS 的跨设备通信默认走端到端加密只有同一账号的可信设备之间才能建立连接。数据不经过第三方服务器中转传输路径完全在本地局域网或设备直连通道内。此外用户可以在设置中随时查看和管理可信设备列表随时撤销某台设备的访问权限。Q6可以同时往多台设备发送内容吗支持但体验上建议一次只流转到一台设备。想象一下你点了一下分享结果手机、平板、电视、手表同时开始播放同一个视频——那画面太美我不敢看。技术上系统支持建立多个并发 Session但从产品体验角度我们推荐用户在弹出的设备选择列表中一次选一台确认后再流转。Q7流转会消耗很多流量吗基本不会。流转走的是局域网直连WiFi Direct 或同一 WiFi 网络不消耗移动数据流量。只有在跨网络的特殊场景如手机使用移动数据、平板连接另一个 WiFi下数据才会走云端中转此时才会消耗流量。但这类场景较少见大多数流转发生在家里或办公室的同一网络下。七、运行效果设备发现与流转全流程八、扩展方向1. 从单设备流转到多设备协同目前的流转是一对一手机 → 平板。但未来可以扩展为一对多接力。比如你在手机上开始导航走到停车场换成手表继续步行指引上楼后手表又可以流转到电视展示地图全景。这就需要一个任务协调器来管理多设备的交接顺序。这种多跳流转Multi-hop Transfer的实现逻辑并不复杂每个流转节点保存上游和下游信息形成一条链表。每当任务流转到下一台设备上一台设备就把自己标记为已完成并指向新设备。如果用户在某台设备上主动返回上一级系统就顺着链路反向追溯找到当前活跃的节点并从那里继续。整个链路最多支持三跳手机→平板→PC再长的话用户体验反而会变得混乱。2. 从视频续播到应用状态整体迁移视频只是一个小口子。未来完全可以把流转扩展到整个应用状态——你正在手机上编辑文档流转后平板直接打开同一份文档、光标位置一样、批注还在。再进一步想象一下你在手机上打了半小时字流转到 PC 后焦点直接跳到打字位置这才是真正的工作流不中断。3. AI 加持的智能流转决策目前需要用户手动选择目标设备但未来可以引入 AI 推理比如检测到你带着手表离开了手机自动建议把手表的导航流转过来检测到你坐到了平板旁边自动推荐把视频流转到平板大屏看。体验越来越无感才是流转的终极形态。4. 跨品牌设备的开放流转目前跨设备流转依赖 HarmonyOS 的软总线能力局限性在于只能在 HarmonyOS 生态内工作。随着生态开放协议如 FIDO、ODCP的成熟未来或许能看到 HarmonyOS 设备与 Android、iOS 设备之间的有限流转——比如图片和文档的互传而不需要云端中转。一个更务实的中间路线是云端兜底模式当 HarmonyOS 设备发现目标设备不是同生态时自动降级到云同步如华为云空间。虽然速度不如软总线但在跨品牌场景下提供了可用性保障。这样用户体验就有了一个兜底选项——流转能力不会因为设备生态不同而完全失效。5. 跨 App 的通用流转协议目前流转能力主要在单个 App 内部生效华为视频续播接力只能在华为视频 App 内但未来可以抽象出一套跨 App 通用流转协议。想象一下你在微信里点开一个视频链接看了几分钟想流转到平板流转的不是视频 App 的状态而是这个 URL 链接本身——平板收到后自动用浏览器打开并继续计时。这套协议需要在系统层面定义流转上下文的通用格式包含 URL、打开时间、停留时长、关联参数如文章 ID。任何 App 都可以声明我支持接收流转上下文系统负责路由和解析App 只需要实现一个标准的回调接口即可。这种设计思路和 Android 的 App Link / iOS 的 Universal Link 有相似之处但加上分布式状态同步后威力更大。