在这里插入图片描述

一、我们想解决什么问题

生活里的三个"卡壳"瞬间

先不说技术,聊几个你一定经历过的场景。

场景一:视频接力。 你在地铁上用手机刷完了半小时的视频精华片段,回家后想舒舒服服用平板的大屏幕继续看同一个视频的后续内容。这时候你要怎么做?大概率是:打开平板 → 打开视频 App → 登录账号 → 搜索视频 → 拖进度条。步骤不少,而且如果 App 不支持登录多设备,还要忍受奇怪的提示。

场景二:文档接力。 你在手机上随手拍了一张会议白板的照片,回到工位想用电脑上的修图工具处理一下。以前的选择是:微信文件传输助手、AirDrop、或者数据线直连。每一种都有点麻烦,尤其是当你身边没有 WiFi 的时候。

场景三:导航接力。 你在车上用手机导航,快到家了但导航还在继续,下车后想把导航"甩"到手表上继续步行指引。想象一下这个画面——你拎着东西站在小区门口,手机屏幕亮了半天,定位飘来飘去,结果还得重新输入目的地。

这三个场景的共同点是:任务在一台设备上开始,但用户想在另一台设备上结束。没有跨设备流转,你就得手动"交接";有了跨设备流转,系统自动帮你把上下文传递过去,对用户来说感觉就像——视频从来没有停过。

技术上的"卡点"在哪里

好,有了需求,我们来看看技术上卡在哪里。

第一,设备发现。你要把视频传给平板,首先得知道平板在哪。手机周围可能有电视、耳机、音箱、平板……系统要能认出谁是你的"自己人",而不是邻居的设备。这需要一个可靠的设备发现机制。

第二,数据传递。视频文件动不动几百 MB,直接走蓝牙太慢,走云端太贵,最好能走局域网直连。更重要的是,传递的不只是文件本身——还有"播放到第几分钟第几秒"这样的状态信息。

第三,一致性。两台设备看到的内容要一致,手机上暂停了,平板上也得暂停;手机上切到了某个进度,平板上打开也要从同一个位置开始。

第四,权限与安全。不是所有设备都值得信任,也不是所有数据都应该被分享。HarmonyOS 通过"同一华为账号 + 同一网络"的双重校验来确保安全。

理解了这四个卡点,后面的设计你就明白为什么这么做了。


二、数据模型设计

先建模,再写代码

技术方案的第一步,永远是先把数据模型定义清楚。就像盖房子之前先画结构图,先把"有什么东西""东西长什么样"说清楚,后面的代码才有根可循。

我们不需要搞什么复杂的类继承,TypeScript 的 interface 就是干这个的——清晰、直观、够用。

设备信息:DeviceInfo

想象你走进一个会议室,里面坐了好几个人,你要先认识他们,才能决定跟谁合作。DeviceInfo 就是设备的"身份证":

// 设备信息接口
interface DeviceInfo {
  deviceId: string;        // 设备唯一标识,UUID 格式
  deviceName: string;      // 用户给设备起的名字,如"小明的小米平板"
  deviceType: DeviceType;  // 设备类型:手机/平板/PC/手表等
  networkType: NetworkType; // 网络类型:WiFi / 5G / 离线
  isTrusted: boolean;      // 是否为可信设备(同账号且已授权)
  batteryLevel: number;    // 当前电量,0~100
}

// 设备类型枚举
enum DeviceType {
  PHONE = 'phone',
  TABLET = 'tablet',
  PC = 'pc',
  WATCH = 'watch',
  TV = 'tv',
  UNKNOWN = 'unknown'
}

// 网络类型枚举
enum NetworkType {
  WIFI = 'wifi',
  CELLULAR = 'cellular',
  DISCONNECTED = 'disconnected'
}

流转会话:TransferSession

设备认识完了,接下来要建立一条"任务通道"。就像你和同事之间建一个工作群,群里定义好:这个任务从哪来、传给谁、传什么内容、进度如何。

// 流转会话接口
interface TransferSession {
  sessionId: string;          // 会话唯一 ID
  sourceDevice: DeviceInfo;   // 发送端设备
  targetDevice: DeviceInfo;   // 接收端设备
  contentType: ContentType;   // 内容类型:视频/图片/文档/链接
  contentUri: string;          // 内容 URI(可以是本地路径或分布式路径)
  contentMeta: ContentMeta;    // 内容元数据
  status: TransferStatus;     // 当前状态:准备/传输中/完成/失败
  progress: number;           // 传输进度,0~100
  createdAt: number;          // 创建时间戳
  expiresAt: number;          // 会话过期时间(避免资源泄漏)
}

// 内容元数据
interface ContentMeta {
  title: string;          // 内容标题
  duration?: number;      // 视频/音频时长(秒)
  position?: number;      // 播放位置(秒),用于视频续播
  fileSize?: number;      // 文件大小(字节)
  mimeType: string;       // MIME 类型
}

// 传输状态枚举
enum TransferStatus {
  IDLE = 'idle',
  PREPARING = 'preparing',
  TRANSFERRING = 'transferring',
  COMPLETED = 'completed',
  FAILED = 'failed',
  CANCELLED = 'cancelled'
}

内容类型:ContentType

流转的内容五花八门,笼统传一个大文件包不够细致,我们需要细分:

// 内容类型枚举
enum ContentType {
  VIDEO = 'video',         // 视频文件
  IMAGE = 'image',         // 图片文件
  DOCUMENT = 'document',   // 文档(PDF/Word/Excel 等)
  URL = 'url',             // 链接(网页/App 页面)
  AUDIO = 'audio',         // 音频文件
  LOCATION = 'location',   // 地理位置(用于导航接力)
}

小结:模型设计的思路

你可能发现了,这三个接口层层递进:DeviceInfo 解决"设备是谁"的问题,TransferSession 解决"任务怎么跑"的问题,ContentType 解决"传的是什么"的问题。先把架子搭好,后面的代码就像填空题一样简单。


三、核心设计决策

方案对比:为什么我们这样设计

技术选型不是"最先进最好",而是"最适合当前场景"。我们来看几个关键决策点。

决策一:传输协议选 SoftBus 还是 HTTP?

对比维度 SoftBus(分布式软总线) HTTP 轮询/推送
传输速度 内网极快(百兆文件秒传) 依赖服务器,较慢
离线支持 同一华为账号可离线传递 必须有外网
开发成本 HarmonyOS 原生封装,开箱即用 需要自建服务
适用场景 大文件(视频/图片) 小数据(状态同步)

我们的结论是:以 SoftBus 为主,HTTP 作为兜底。大文件走软总线,享受极致速度;小状态走轻量协议,保证可用性。

决策二:状态存储用本地还是分布式数据服务?

跨设备流转最怕的就是"手机停了、平板不知道从哪开始"。HarmonyOS 提供了分布式数据服务(Distributed Data Service),能让多台设备共享同一份数据,而且自动同步。

// 选型对比
const isLocalOnly = false;     // ❌ 只存本地:另一台设备拿不到状态
const isCloudSync = true;      // ⚠️ 云同步:慢,依赖网络,还收费
const isDistributedDB = true;  // ✅ 分布式数据库:本地化速度,跨设备同步

决策三:发现设备用蓝牙还是 WiFi?

蓝牙功耗低但速度慢,适合配对阶段;WiFi 速度快,适合大数据传输。HarmonyOS 的做法是:先用蓝牙低功耗广播发现设备,再用 WiFi Direct 建立高速通道传输数据。扬长避短,效率最高。

决策四:用户体验上,推送制还是拉取制?

  • 推送制(手机主动推给平板):手机控制感强,但平板可能不想收(比如正在打游戏)。
  • 拉取制(平板主动从手机拉数据):平板自主可控,但需要平板主动发起。
  • 我们的选择用户确认 + 智能推荐。检测到用户在两台设备上都有同一个 App 时,弹出"是否继续在平板上观看?"的提示,让用户自己决定。

四、完整代码实现

代码文件一:设备发现服务 DeviceDiscovery.ets

这是整个流转的"入口"。我们用 HarmonyOS 提供的分布式能力,先找到附近的设备:

// DeviceDiscovery.ets - 设备发现服务
import deviceManager from '@ohos.distributedHardware.deviceManager';

class DeviceDiscovery {
  private dmInstance: deviceManager.DeviceManager | null = null;
  private deviceList: DeviceInfo[] = [];

  // 初始化设备管理器
  initialize() {
    deviceManager.createDeviceManager('com.atomgit.app', (err, dm) => {
      if (err) {
        console.error('设备管理器创建失败:', err);
        return;
      }
      this.dmInstance = dm;
      this.startDiscovery();
    });
  }

  // 开始发现可信设备
  startDiscovery() {
    if (!this.dmInstance) return;

    // 获取同一华为账号下的可信设备列表
    const trustedDevices = this.dmInstance.getTrustedDeviceListSync();
    this.deviceList = trustedDevices.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[] {
    const myDeviceId = this.dmInstance?.getLocalDeviceInfoSync()?.deviceId;
    return this.deviceList.filter(d => d.deviceId !== myDeviceId);
  }

  private mapDeviceType(type: number): DeviceType {
    const map: Record<number, DeviceType> = {
      0x00: DeviceType.PHONE,
      0x01: DeviceType.TABLET,
      0x02: DeviceType.PC,
      0x03: DeviceType.WATCH,
      0x04: DeviceType.TV,
    };
    return map[type] || DeviceType.UNKNOWN;
  }
}

代码文件二:流转服务 TransferService.ets

设备找到了,接下来就是建一条流转通道,把内容送过去:

// TransferService.ets - 跨设备流转服务
import distributedChannel from '@ohos.distributedHardware.channel';
import fileIO from '@ohos.fileio';

class TransferService {
  private session: distributedChannel.DistributedChannel | null = null;

  // 创建流转会话
  async createSession(source: DeviceInfo, target: DeviceInfo, content: ContentMeta): Promise<TransferSession> {
    const sessionId = this.generateUUID();

    // 建立跨设备通信通道(走 SoftBus)
    this.session = await distributedChannel.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 分钟后过期
    };
  }

  // 传输文件(带进度回调)
  async transferFile(session: TransferSession, filePath: string,
                     onProgress: (pct: number) => void): Promise<void> {
    if (!this.session) throw new Error('会话未建立');

    session.status = TransferStatus.TRANSFERRING;

    // 打开本地文件,通过分布式通道发送
    const fd = fileIO.open(filePath, fileIO.OpenMode.READ_ONLY);
    const fileSize = fileIO.statSync(filePath).size;
    let sent = 0;

    const buffer = new ArrayBuffer(8192);
    let bytesRead = 0;

    // 分块传输,每传完一块更新进度
    while ((bytesRead = fileIO.read(fd, buffer)) > 0) {
      await this.session.write(buffer.slice(0, bytesRead));
      sent += bytesRead;
      const pct = Math.floor((sent / fileSize) * 100);
      onProgress(pct);
      session.progress = pct;
    }

    fileIO.close(fd);
    session.status = TransferStatus.COMPLETED;
    console.info('文件传输完成,sessionId:', session.sessionId);
  }

  private generateUUID(): string {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, c => {
      const r = Math.random() * 16 | 0;
      return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
  }
}

代码文件三:续播接力 VideoContinuity.ets

传完文件还不够,关键是"从哪继续看"。这段代码管理播放位置的记录和恢复:

// VideoContinuity.ets - 视频续播接力
import distributedKV from '@ohos.distributedKvStore';

class VideoContinuity {
  private kvStore: distributedKV.KVStore | null = null;
  private readonly KV_ID = 'video_position_db';

  // 初始化分布式键值数据库
  async init() {
    const options: distributedKV.Options = {
      createIfMissing: true,
      kvStoreType: distributedKV.KVStoreType.SINGLEVERSION
    };
    this.kvStore = await distributedKV.createKVStore(this.KV_ID, options);
  }

  // 记录当前播放位置(每次播放进度变化时调用)
  async savePosition(videoId: string, position: number, deviceId: string) {
    if (!this.kvStore) await this.init();

    const record = {
      videoId,
      position,          // 秒为单位
      deviceId,          // 记录是哪台设备保存的
      timestamp: Date.now()
    };

    await this.kvStore.put(`pos_${videoId}`, JSON.stringify(record));
    console.info(`保存播放位置: ${videoId} -> ${position}s`);
  }

  // 获取续播位置(打开视频时调用)
  async getResumePosition(videoId: string): Promise<number | null> {
    if (!this.kvStore) await this.init();

    const raw = await this.kvStore.get(`pos_${videoId}`);
    if (!raw) return null;

    const record = JSON.parse(raw);
    // 只接受 24 小时内的记录(避免返回过时位置)
    const maxAge = 24 * 60 * 60 * 1000;
    if (Date.now() - record.timestamp > maxAge) return null;

    return record.position;
  }

  // 清除记录(用户主动从头播放时调用)
  async clearPosition(videoId: string) {
    if (!this.kvStore) return;
    await this.kvStore.delete(`pos_${videoId}`);
  }
}

小结:三个文件的职责划分

设备发现找到"谁是我的小伙伴",流转服务负责"怎么把东西送过去",续播接力管理"到了新设备从哪接上"。三个模块各司其职,通过统一的 Session 数据结构串联起来。


五、深度技术原理

软总线:设备之间的"隐形高速公路"

好,理解了代码之后,我们来聊聊背后的原理。

想象一下:你的手机和平板之间,没有一根数据线,也没有云端服务器做中转,那它们是怎么"对话"的?

答案就是 HarmonyOS 的分布式软总线(SoftBus)

打个比方。普通的设备互联就像这样:你从深圳寄一个包裹到广州,先把包裹送到北京的总仓库(北京、上海、深圳三个仓库共用一个大系统),总仓库再转发到广州。绕了一大圈,速度慢,还贵。

软总线不一样,它更像是在深圳和广州之间建了一条隐形直达隧道。两座城市之间直接拉一根光纤,不需要绕到北京,速度快了好几倍。这条"隧道"可以走 WiFi,可以走 USB,甚至未来可以走蓝牙——但对开发者来说,这些细节完全被屏蔽了,你只需要"发送"和"接收",就像读写本地文件一样简单。

超级终端:信任关系的建立

你可能会问:万一邻居的手机偷偷往我平板上发东西怎么办?

这就涉及到"超级终端"的信任体系了。HarmonyOS 的设备信任关系建立在华为账号上。只有满足以下两个条件的设备,才能互相发现和通信:

  1. 同一华为账号登录。手机和平板登录的是同一个华为账号,系统认定这两台设备属于同一个人。
  2. 同一局域网或近距离。设备需要在同一个 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 有相似之处,但加上分布式状态同步后威力更大。


Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐