在这里插入图片描述

1 -> 概述

1.1 -> 瓦片图层功能的技术演进

鸿蒙Map Kit中的瓦片图层(TileOverlay)是地图服务体系中一个非常重要的扩展能力。回顾一下这个功能的发展历程:从5.0.3(15)版本开始,Map Kit首次支持瓦片图层功能,为开发者带来了在地图上叠加自定义瓦片数据的能力。再到6.0.0(20)版本,瓦片数据缓存功能正式上线,这标志着Map Kit在离线场景和性能优化方面迈出了重要一步。而到了6.1.1(24)版本,系统更进一步支持了高层级复用低层级瓦片的规则,意味着在不同缩放级别之间可以更加高效地复用瓦片数据,进一步优化了内存和磁盘占用。

坦白说,这三个版本迭代解决了不少实际开发中的痛点。最早只有在线下载方式的时候,每次地图平移或缩放都要重新请求网络,流量消耗大、加载延迟高,在弱网环境下体验尤其糟糕。6.0.0引入的缓存机制直接从底层解决了这个问题——瓦片数据一旦下载过就会被保存在本地,下次加载同样的区域时几乎瞬时呈现。而6.1.1的高层级复用规则则更进一步:当你从低缩放级别(比如看到整个城市)放大到高缩放级别(看到街道详情)时,系统会自动复用低层级的瓦片数据,而不是全部重新加载。

1.2 -> 什么是瓦片图层

瓦片图层是显示在地图上的一组图像(Tile),这些瓦片以网格形式平铺,覆盖在地图底图之上。与传统的覆盖物(如Marker、Polyline等)不同,瓦片图层会随地图的平移、缩放、旋转等操作做相应的变换,并且瓦片图层位于底图之上,可以遮挡底图,但不遮挡其他覆盖物图层。

瓦片图层最有价值的应用场景是什么呢?试想一下,你拥有一个商场的内部地图数据,希望在地图上覆盖显示每一层的店铺分布——这就是典型的瓦片图层应用场景。又或者你开发了一个旅游景区应用,希望将景区的徒步路线图叠加在标准地图上,瓦片图层同样是最佳选择。概括来说,只要开发者拥有某一特定区域的地图数据,并希望用这些数据覆盖对应位置的华为地图,瓦片图层就是最直接的实现方式。

1.3 -> 本文目标

本文将围绕华为鸿蒙6.0 Map Kit中瓦片图层的新增特性展开,重点讨论两个方面:一是本地加载方式的完整实现路径,包括如何通过tileProvider接口从应用资源、本地文件系统或其他本地存储中读取瓦片数据;二是瓦片数据缓存能力的使用方法,涵盖内存缓存和磁盘缓存两个层面,包括缓存开关控制、缓存大小限制以及缓存的清理策略。通过本文,你将能够掌握在鸿蒙应用中灵活运用瓦片图层的完整技术方案。

1.4 -> 版本与系统要求

瓦片图层功能从以下版本开始可用:

  • 瓦片图层基本功能:API版本 5.0.3(15) 开始支持
  • 瓦片数据缓存功能:API版本 6.0.0(20) 开始支持
  • 高层级复用低层级瓦片:API版本 6.1.1(24) 开始支持

系统能力要求:SystemCapability.Map.Core

模型约束:此接口仅可在Stage模型下使用。元服务API:从版本4.1.0(11)开始支持在元服务中使用。

2 -> 核心概念与API体系

2.1 -> 瓦片坐标系统

在深入代码之前,有必要先厘清瓦片图层的核心坐标概念。瓦片地图采用四叉树金字塔模型,每个瓦片由三个参数唯一确定:

  • x(行索引):瓦片在水平方向上的编号
  • y(列索引):瓦片在垂直方向上的编号
  • z(缩放级别):瓦片所属的地图层级,范围通常为0-20+

简单来说,z越小,地图看到的范围越大但细节越少;z越大,地图越精细但覆盖的范围越小。每个缩放级别的瓦片数量呈4倍递增:z=0时只有1张瓦片,z=1时有4张,z=2时有16张,以此类推。

理解这套坐标系统之所以重要,是因为无论是从网络URL加载还是从本地读取瓦片,都需要正确传递x、y、z三个坐标参数,系统才会知道请求的是哪一块地图数据。

2.2 -> 核心API类说明

瓦片图层功能主要由以下几个核心类提供:

TileOverlayParams
瓦片图层的核心参数配置类,用于定义瓦片数据的来源(URL或Provider)、缓存策略等。在线下载方式下,需要在此配置tileUrl;本地加载方式下,需要配置tileProvider。

TileOverlayOptions
瓦片图层的样式和行为选项类,包括淡入淡出效果开关、透明度、可见性等配置。

addTileOverlay
MapComponentController中提供的方法,用于为地图增加瓦片图层,返回TileOverlay实例。

TileOverlay
瓦片图层实例,支持更新和查询相关属性,也提供了缓存清理等管理方法。

2.3 -> 两种瓦片获取方式对比

在Map Kit中,瓦片图层支持两种数据获取方式:

特性 在线下载 本地加载
数据来源 网络URL 应用本地资源/文件系统
配置方式 tileUrl字符串 tileProvider回调函数
网络依赖 需要网络 不需要网络
缓存机制 自动缓存(需开启) 由开发者自行控制
适用场景 动态瓦片服务、第三方瓦片服务 内置离线瓦片包、固定区域数据

两种方式的本质区别在于数据源:在线下载方式由Map SDK根据URL格式自动请求网络获取瓦片图片,而本地加载方式则需要开发者自行实现瓦片的读取逻辑,灵活性更高,但也意味着更多的实现工作。

3 -> 本地加载方式详解

3.1 -> 技术背景与设计思路

本地加载方式的理论依据很直接:并不是所有瓦片数据都适合从网络实时下载。实际开发中至少会遇到以下几种场景:

  • 应用需要内置固定的地图资源:比如一本旅游指南应用,内置了景区的手绘地图瓦片包
  • 网络环境不可用:用户处于飞行模式或弱网环境,仍希望看到部分地图内容
  • 敏感数据本地处理:瓦片数据涉及地理信息保密要求,不能通过网络传输

本地加载的核心设计思路是:通过TileOverlayOptions.tileProvider回调接口,由应用自主控制瓦片的获取逻辑。系统在渲染地图的某个瓦片区域时,会调用tileProvider方法,传入当前视图所需瓦片的x、y、z坐标,开发者需要在这个方法中返回对应的Image数据或Promise。整个过程类似于一个“按需供应”的模式——地图滚动到哪里,tileProvider就被调用到哪里。

3.2 -> 完整代码示例

下面给出一个完整的本地加载实现,涵盖从模块导入到瓦片加载的全过程。

3.2.1 -> 步骤1:导入所需模块

import { map, mapCommon, MapComponent } from '@kit.MapKit';
import { AsyncCallback } from '@kit.BasicServicesKit';
import { resourceManager } from '@kit.LocalizationKit';

3.2.2 -> 步骤2:声明组件变量

@Entry
@Component
struct TileOverlayDemo {
  private mapOption?: mapCommon.MapOptions;
  private mapController?: map.MapComponentController;
  private callback?: AsyncCallback;
  private tileOverlay?: map.TileOverlay;
}

3.2.3 -> 步骤3:初始化地图配置

在aboutToAppear生命周期中配置地图初始化参数,并注册地图加载完成的回调:

aboutToAppear(): void {
  // 地图初始化参数,设置地图中心点及缩放层级
  this.mapOption = {
    position: {
      target: { latitude: 39.9, longitude: 116.4 },
      zoom: 15
    }
  };
  
  // 地图初始化回调
  this.callback = async (err: Error, mapController: map.MapComponentController) => {
    if (!err) {
      this.mapController = mapController;
      // 在地图加载完成后添加瓦片图层
      this.addLocalTileOverlay();
    }
  };
}

3.2.4 -> 步骤4:实现本地瓦片加载方法

这是本地加载的核心部分。根据鸿蒙官方文档,本地加载需要开发者自行实现tileProviderMethod方法:

// 本地瓦片加载方法的核心实现
private async tileProviderMethod(x: number, y: number, z: number): Promise<image.PixelMap> {
  return new Promise(async (resolve, reject) => {
    try {
      // 方式1:从应用资源目录加载瓦片图片(需要预先放置瓦片文件)
      // 假设瓦片文件命名规则为 tile_z_x_y.png,存放在 resources/rawfile/tiles/ 目录下
      const resourceMgr = getContext(this).resourceManager;
      const fileName = `tiles/tile_${z}_${x}_${y}.png`;
      
      // 尝试读取RawFile资源
      const rawFileData = await resourceMgr.getRawFileContent(fileName);
      
      if (rawFileData && rawFileData.byteLength > 0) {
        // 将RawFile数据转换为PixelMap
        const imageSource = image.createImageSource(rawFileData.buffer);
        const pixelMap = await imageSource.createPixelMap();
        resolve(pixelMap);
      } else {
        // 瓦片不存在,返回空白图或拒绝
        reject(new Error(`Tile not found: ${fileName}`));
      }
    } catch (error) {
      console.error(`Failed to load tile (${z}/${x}/${y}):`, error);
      reject(error);
    }
  });
}

3.2.5 -> 步骤5:配置并添加本地瓦片图层

private addLocalTileOverlay(): void {
  // 配置瓦片图层参数(核心:指定tileProvider)
  const tileOptions: mapCommon.TileOverlayOptions = {
    tileProvider: this.tileProviderMethod,   // 本地获取瓦片方式,需开发者自行实现
    fadeIn: true,                             // 开启淡入淡出效果
    transparency: 0.5,                        // 透明度,取值范围0-1
    visible: true                             // 瓦片图层可见
  };
  
  // 瓦片图层参数(缓存相关配置在后面详细说明)
  const tileParams: mapCommon.TileOverlayParams = {
    // 注意:本地加载方式下,tileUrl可以不填或留空
    // 因为瓦片数据由tileProvider提供
  };
  
  if (this.mapController !== undefined) {
    try {
      this.tileOverlay = this.mapController.addTileOverlay(tileParams, tileOptions);
      console.info('Local tile overlay added successfully');
    } catch (error) {
      console.error('Failed to add tile overlay:', error);
    }
  }
}

3.3 -> tileProvider方法中的坐标变换细节

深入理解tileProvider方法会发现,x、y、z三个参数直接来自地图当前的视图范围。当用户在屏幕上拖动地图或缩放时,系统会在每一帧渲染前计算出当前视口范围内需要显示哪些瓦片,然后逐个调用tileProvider方法。正因为调用频率可能非常高,实现时务必注意性能:

  • 瓦片图片的读取操作应尽可能快,避免在主线程执行耗时操作
  • 如果瓦片来自网络(虽然是本地加载场景),但需要网络访问,应考虑异步加载
  • 如果瓦片图片重复出现(比如边缘瓦片被复用),建议在tileProvider内部实现二次缓存

如果瓦片因各种原因(文件不存在、格式错误、读取失败等)无法提供,建议reject并记录错误日志。系统在收到reject后会尝试下一个可用的瓦片源,或显示空白区域。

3.4 -> 瓦片预置的工程配置

本地瓦片需要提前放置到应用的特定目录中。下面是工程结构建议:

entry/
├── src/
│   ├── main/
│   │   ├── ets/
│   │   │   └── pages/
│   │   │       └── TileOverlayPage.ets
│   │   ├── resources/
│   │   │   ├── rawfile/
│   │   │   │   └── tiles/           ← 瓦片文件放置目录
│   │   │   │       ├── tile_10_100_200.png
│   │   │   │       ├── tile_10_101_200.png
│   │   │   │       └── ...
│   │   │   └── ...

同时需要确保在module.json5中声明必要的权限。本地加载虽然不涉及网络请求,但如果瓦片需要从外部存储读取,则可能需要存储权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET",      // 如后续需要网络瓦片补充
        "reason": "$string:internet_reason",
        "usedScene": { "abilities": [...] }
      },
      {
        "name": "ohos.permission.READ_MEDIA_IMAGES",  // 如需从图库读取瓦片
        "reason": "$string:read_media_reason"
      }
    ]
  }
}

4 -> 瓦片数据缓存能力详解

4.1 -> 缓存能力概述

缓存能力是6.0.0版本瓦片图层的重磅级新增特性。开启缓存后,瓦片数据一旦从网络或本地加载过,就会被持久化存储,后续再次请求相同坐标的瓦片时可直接从缓存读取,避免了重复的网络请求或重复的本地I/O操作。

从整体架构上看,Map Kit的瓦片缓存采用了两层缓存机制

  • 内存缓存:缓存最近使用的瓦片PixelMap对象,访问速度最快,容量受限
  • 磁盘缓存:将瓦片图片持久化存储在应用沙箱中,应用重启后仍可复用

4.2 -> 缓存配置参数详解

当使用在线下载方式进行瓦片加载时,可以通过TileOverlayParams配置缓存选项。需要说明的是,本地加载方式的缓存控制权在开发者自己手中(tileProvider内的缓存策略自行设计),而在线下载方式的缓存则由Map SDK统一管理。

根据鸿蒙官方文档的示例,缓存配置参数如下:

const tileParams: mapCommon.TileOverlayParams = {
  tileUrl: "https://your-tile-server.com/tile?x={x}&y={y}&z={z}",
  
  // 是否开启磁盘缓存
  // true: 开启磁盘缓存,false: 关闭磁盘缓存
  diskCacheEnabled: true,
  
  // 磁盘缓存大小限制,单位:KB
  // 默认大小为20480KB(约20MB)
  diskCacheSize: 20480,
  
  // 存放磁盘缓存的沙箱路径
  // 通常使用应用的应用特定目录,如databaseDir
  diskCachePath: this.getUIContext().getHostContext()?.databaseDir
};

各参数的取值逻辑和注意事项:

diskCacheEnabled(是否开启磁盘缓存)
默认建议设置为true。关闭磁盘缓存意味着每次瓦片请求都会通过网络重新下载,不推荐在生产环境中使用。

diskCacheSize(磁盘缓存大小限制)
单位是KB,默认20480KB(约20MB)。这个值的设定需要权衡设备存储空间与瓦片覆盖范围。对于城市级地图数据(z=10~15级别),20MB大约可以存储几百到上千张瓦片图片,基本覆盖常见城市核心区域。如果应用覆盖的地域范围较大,可以适当增大这个值,比如设置为51200KB(50MB)。

diskCachePath(磁盘缓存存放路径)
建议使用应用的沙箱数据库目录或缓存目录。这类目录在应用卸载时会自动清理,不会造成存储残留。路径必须是可写的且具有足够剩余空间。

4.3 -> 在线下载方式开启缓存的完整代码

为了完整呈现缓存配置的上下文,下面给出一个启用缓存的在线下载方式完整示例:

@Entry
@Component
struct OnlineTileOverlayDemo {
  private mapOption?: mapCommon.MapOptions;
  private mapController?: map.MapComponentController;
  private callback?: AsyncCallback;
  private tileOverlay?: map.TileOverlay;
  
  aboutToAppear(): void {
    this.mapOption = {
      position: {
        target: { latitude: 39.9, longitude: 116.4 },
        zoom: 12
      }
    };
    
    this.callback = async (err: Error, mapController: map.MapComponentController) => {
      if (!err) {
        this.mapController = mapController;
        this.addCachedTileOverlay();
      }
    };
  }
  
  private addCachedTileOverlay(): void {
    // 配置瓦片图层的参数(重点:缓存配置在这里)
    const tileParams: mapCommon.TileOverlayParams = {
      tileUrl: "https://your-tile-server.com/tile?x={x}&y={y}&z={z}",
      diskCacheEnabled: true,        // 开启磁盘缓存
      diskCacheSize: 20480,          // 磁盘缓存大小限制为20MB
      diskCachePath: this.getUIContext().getHostContext()?.databaseDir
    };
    
    const tileOptions: mapCommon.TileOverlayOptions = {
      tileProvider: undefined,       // 使用tileUrl时不需要tileProvider
      fadeIn: true,
      transparency: 0,
      visible: true
    };
    
    if (this.mapController !== undefined) {
      this.tileOverlay = this.mapController.addTileOverlay(tileParams, tileOptions);
      console.info('Cached tile overlay added');
    }
  }
  
  build() {
    Stack() {
      MapComponent({ mapOptions: this.mapOption, mapCallback: this.callback })
        .width('100%')
        .height('100%')
    }
    .height('100%')
  }
}

4.4 -> 缓存的生命周期与清理机制

缓存的磁盘文件会一直保留,直到达到以下任一条件时才会被清理:

  • 缓存总大小超过diskCacheSize:超过限制后,按照某种策略(如LRU,即Least Recently Used)淘汰旧瓦片
  • 应用主动调用清理接口:调用了clearTileCache()或clearDiskCache()
  • 应用卸载:沙箱目录随应用一并删除

Map Kit提供了两种缓存清理粒度:

clearTileCache():清除指定瓦片图层的内存缓存,磁盘缓存不受影响。

clearDiskCache():清除磁盘和内存中的所有缓存数据。

使用示例:

// 清除指定瓦片图层的内存缓存
if (this.tileOverlay) {
  this.tileOverlay.clearTileCache();
}

// 清除磁盘缓存和内存缓存(彻底清理)
if (this.tileOverlay) {
  this.tileOverlay.clearDiskCache();
}

// 移除瓦片图层(可选)
if (this.tileOverlay) {
  this.tileOverlay.remove();
}

关于这两者的区别,简单来说:clearTileCache()是“轻量级”清理,适合在视图切换、临时释放内存等场景使用;clearDiskCache()是“重量级”清理,适合应用退出前、用户主动清理缓存数据、瓦片数据版本更新等场景使用。

4.5 -> 高层级复用低层级瓦片规则(6.1.1+)

从6.1.1(24)版本开始,Map Kit引入了高层级复用低层级瓦片规则。这个功能理解起来并不复杂:当地图从低缩放级别(如z=12,看到城市全景)放大到高缩放级别(如z=15,看到街道细节)时,系统会自动复用低层级的瓦片数据作为高缩放级别的基础瓦片,直到高分辨率的瓦片加载完成后才进行替换。

这一机制带来的好处非常直接:

  • 减少瓦片请求数量:不再需要为每个高缩放级别单独请求所有瓦片
  • 降低网络流量消耗:特别是在从低缩放级别逐级放大的过程中,流量节省效果很明显
  • 提升用户体验:地图放大时不会出现长时间的白块等待,而是先显示复用的低层级瓦片(模糊但可见),再逐渐替换为精细瓦片

开发者不需要为这个功能做任何额外配置,系统会自动处理。

5 -> 两种加载方式的完整对比

对比维度 在线下载方式 本地加载方式
数据来源 网络URL 应用本地(RawFile/文件系统)
配置参数 需要配置tileUrl字符串 需要实现tileProvider回调
缓存机制 内置自动缓存(可配置) 需自行实现
网络依赖 初始加载需要网络 完全离线可用
更新灵活性 瓦片服务器更新即可生效 需要应用版本更新
性能特点 首屏加载慢,后续有缓存加速 首屏加载快(预置数据)
适用场景 动态数据、第三方瓦片服务、大规模区域 内置离线包、固定小区域、网络受限场景
版本支持 5.0.3+ 5.0.3+

选择哪种方式取决于具体的业务需求:

  • 如果你的瓦片数据范围很大(如全国地图数据)、需要频繁更新、或者来自第三方瓦片服务,在线下载+缓存是更合理的选择
  • 如果瓦片数据是某个固定区域(如一个商场、一个景区)且不需要动态更新,本地加载方式可以带来更好的首次加载体验和离线可用性

这里需要补充一点:两种方式并不是互斥的,你可以在同一个地图上同时添加多个瓦片图层——有些用在线下载方式,有些用本地加载方式。系统会按照添加顺序进行叠加渲染,但需要注意瓦片图层彼此之间会相互遮挡。

6 -> 常见问题与注意事项

6.1 -> 瓦片图层的覆盖顺序

瓦片图层位于底图之上,会遮挡底图,但不遮挡Marker、Polyline等其他类型的覆盖物。添加多个瓦片图层时,后添加的图层会覆盖在先添加的图层之上。如果你发现某个瓦片图层被另一个图层盖住了,可以考虑调整添加顺序或调用TileOverlay的相关方法调整层级。

6.2 -> 瓦片URL中的占位符格式

在在线下载方式中,瓦片URL的格式要求非常严格。占位符必须是{x}{y}{z}的精确形式,大小写敏感,不能有其他格式。根据开发者社区中的反馈,这是一个容易被忽略的问题。

✅ 正确格式:

https://your-server.com/tile?z={z}&x={x}&y={y}

❌ 错误格式:

https://your-server.com/tile?z=%z%&x=%x%&y=%y%
https://your-server.com/tile?level={level}&col={col}&row={row}

6.3 -> 地图重建后瓦片图层会丢失

在使用Map Kit过程中需要注意:如果地图组件被销毁并重新创建,地图上的瓦片图层会被自动丢弃,需要手动重新添加。常见的场景包括页面切换后返回、应用从后台切回前台时地图视图被重建等。建议的做法是将瓦片图层的配置参数持久化(如保存在@Storage中),在地图初始化回调中根据保存的配置重新添加瓦片图层。

6.4 -> 瓦片图片格式与尺寸规范

Map Kit对于瓦片图片的格式和尺寸有一定要求:

  • 图片格式:支持PNG、JPEG等常见格式
  • 推荐尺寸:256×256像素或512×512像素
  • 图片类型:支持透明背景(如PNG),方便与底图融合

如果瓦片尺寸不是标准尺寸,可能会出现位置偏移或显示异常的问题。

7 -> 性能优化建议

7.1 -> 瓦片预加载机制

对于需要流畅地图滚动体验的应用,可以考虑在瓦片图层之外实现瓦片预加载机制——提前加载当前视口周边瓦片。具体实现思路:监听地图的onMapMoveEnd事件,获取当前地图视口的边界坐标,计算出周边瓦片索引后提前发起加载或缓存。

7.2 -> 限制最大缩放级别

在离线地图场景中,可以考虑限制地图的最大缩放级别,以平衡渲染效果与性能。例如,如果瓦片数据只支持到z=18级别,可以将地图相机限制在18以内,避免用户放大到无瓦片可用的空白区域。

const mapOptions: mapCommon.MapOptions = {
  position: {
    target: { latitude: 39.9, longitude: 116.4 },
    zoom: 14
  },
  // 限制最大缩放级别(需要查阅MapOptions是否支持,这里为示意思路)
  minZoom: 10,
  maxZoom: 18
};

7.3 -> 合理设置缓存容量

磁盘缓存的大小设置是一个需要根据应用实际使用场景进行权衡的问题:

  • 设置过小:命中率低,频繁重新下载,流量消耗大、加载延迟高
  • 设置过大:占用存储空间,可能被系统或用户清理

建议根据瓦片平均大小、覆盖区域面积、用户常驻地等因素综合估算。一张标准256×256 PNG瓦片的大小通常在10KB-50KB之间,20MB的缓存大约可以存储400到2000张瓦片,足以覆盖多数城市级应用的核心区域。

8 -> 总结

本文详细介绍了鸿蒙6.0 Map Kit中瓦片图层的本地加载方式和瓦片数据缓存能力,可以归纳为以下几个要点:

技术演进:瓦片图层功能从5.0.3的初步支持,到6.0.0的缓存能力引入,再到6.1.1的高层级复用规则,每一次迭代都切中了实际开发中的痛点问题。

本地加载:通过tileProvider回调接口,开发者可以完全控制瓦片数据的来源和加载逻辑。无论是预置在应用安装包中的瓦片包,还是存储在本地文件系统中的离线瓦片,都可以通过tileProvider灵活接入。

缓存能力:6.0.0新增的磁盘缓存功能直接解决了在线下载方式下的重复网络请求问题。通过简单的参数配置即可开启缓存,同时提供了缓存大小限制和路径自定义的能力,便于开发者根据实际场景灵活调整。

API设计:TileOverlayParams和TileOverlayOptions两个配置类将数据源配置与样式配置清晰分离,同时支持在线下载和本地加载两种模式,设计上具有较强的扩展性。

在实际开发中,建议根据以下原则进行技术选型:

  • 瓦片数据范围动态更新来自第三方服务 → 在线下载 + 开启缓存
  • 瓦片数据范围固定不变强离线场景 → 本地加载
  • 既有大量在线瓦片,又有少量固定区域离线瓦片 → 两种方式混合使用

最后需要特别指出的是,瓦片图层功能从API版本5.0.3开始支持,而缓存功能需要API版本6.0.0及以上。如果你的应用需要同时兼容多个版本的鸿蒙设备,需要在代码中进行版本判断或功能降级处理。此外,所有地图相关操作都应在MapComponent初始化完成的回调中进行,即在mapCallback中拿到mapController之后再进行addTileOverlay等操作,这是开发初期最容易忽略但必须遵守的规则。


感谢各位大佬支持!!!

互三啦!!!
Logo

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

更多推荐