HarmonyOS基础(四):一次开发多端部署

一次开发多端部署是HarmonyOS面向多终端的核心设计理念,也是鸿蒙生态区别于传统操作系统的关键能力。随着终端设备形态日益多样化,分布式技术逐渐打破单一硬件边界,一个应用或服务可以在不同的硬件设备之间随意调用、互助共享,让用户享受无缝的全场景体验。对开发者而言,广泛的设备类型意味着更广大的潜在用户群体。但如果一个应用需要在多个设备上提供同样的内容,则需要适配不同的屏幕尺寸和硬件,开发成本较高。

HarmonyOS系统面向多终端提供了一多的能力,让开发者可以基于一种设计,高效构建多端可运行的应用。其核心定义是一套代码工程、一次开发上架、多端按需部署。

为实现这一目标,需要解决三个基础问题。不同设备间的屏幕尺寸、色彩风格等存在差异,页面如何适配。不同设备的系统能力有差异,功能如何兼容。如何实现一套代码同时能部署到多种不同设备上,代码工程如何组织。

针对这三个问题,可以从界面级、功能级、工程级三个维度给出解决思路。本文将系统讲解这三个维度的完整技术方案。

一、一多概述与设计理念

1.1 一多的定义与目标

一次开发多端部署是HarmonyOS面向多终端的核心能力。其定义为一套代码工程,一次开发上架,多端按需部署。

一多的目标包含三个层面。支撑开发者快速高效地开发支持多种终端设备形态的应用。实现对不同设备兼容的同时,提供跨设备的流转、迁移和协同的分布式体验。最终帮助开发者以更低的成本覆盖更广泛的设备生态。

1.2 一多的核心价值

一多的核心价值体现在开发效率、用户体验和商业价值三个维度。

从开发效率看,一套代码覆盖多端,避免为每个设备单独开发维护。从用户体验看,应用在不同设备上获得一致且优化的使用体验。从商业价值看,一个应用可以部署到更多设备,触达更广泛的用户群体。

1.3 一多的三个关键问题

一多需要解决三个基础问题。

问题一:页面如何适配。不同设备间的屏幕尺寸、色彩风格等存在差异,页面如何自适应变化。

问题二:功能如何兼容。不同设备的系统能力有差异,如智能穿戴设备是否具备定位能力、智慧屏是否具备摄像头等,功能如何兼容。

问题三:工程如何组织。如何实现一套代码同时能部署到多种不同设备上,代码工程如何组织。

针对这三个问题,可以从界面级、功能级、工程级三个维度给出解决思路。界面级一多解决页面适配问题,功能级一多解决系统能力兼容问题,工程级一多解决代码组织问题。

1.4 一多的两种部署模型

一多有两种部署模型。

部署模型A是不同类型的设备上按照一定的工程结构组织方式,通过一次编译生成相同的HAP或HAP组合。部署模型B是不同类型的设备上按照一定的工程结构组织方式,通过一次编译生成不同的HAP或HAP组合。

开发者可以从应用UX设计及应用功能两个维度,结合具体的业务场景,选择采用哪种部署模型。从屏幕尺寸、输入方式及交互距离三个维度考虑,可以将常用类型的设备分为不同泛类。手机和平板归为一类,车机和智慧屏归为一类,智能穿戴归为一类。

对于相同泛类的设备,优先选择部署模型A。对于不同泛类设备,优先选择部署模型B。如果应用在不同泛类设备上的UX设计或功能相似时,可以使用部署模型A。如果应用在同一泛类不同类型设备上UX设计或功能差异非常大时,可以使用部署模型B。

在实际开发多设备应用时,如果目标设备类型较多,往往是部署模型A和部署模型B混合使用。不管采用哪种部署模型,都应该采用一次编译。

1.5 一多的三层工程结构

一多推荐在应用开发过程中使用三层工程结构。

common层是公共能力层,用于存放公共基础能力集合,如工具库、公共配置等。common层可编译成一个或多个HAR包或HSP包。HAR中的代码和资源跟随使用方编译,如果有多个使用方,它们的编译产物中会存在多份相同拷贝。HSP中的代码和资源可以独立编译,运行时在一个进程中代码也只会存在一份。common层只可以被products和features依赖,不可以反向依赖。

features层是基础特性层,用于存放基础特性集合,如应用中相对独立的各个功能的UI及业务逻辑实现等。各个feature高内聚、低耦合、可定制,供产品灵活部署。不需要单独部署的feature通常编译为HAR包或HSP包,供products或其它feature使用,但是不能反向依赖products层。需要单独部署的feature通常编译为Feature类型的HAP包,和products下Entry类型的HAP包进行组合部署。features层可以横向调用及依赖common层。

products层是产品定制层,用于针对不同设备形态进行功能和特性集成。products层各个子目录各自编译为一个Entry类型的HAP包,作为应用主入口。products层不可以横向调用。

这种分层架构的代码工程结构抽象后一般如下:

application
├── common      # 可选。公共能力层,编译为HAR包或HSP包
├── features    # 可选。基础特性层
│   ├── feature1   # 子功能1,编译为HAR包或HSP包或Feature类型的HAP包
│   ├── feature2   # 子功能2,编译为HAR包或HSP包或Feature类型的HAP包
│   └── ...
└── products    # 必选。产品定制层
    ├── wearable   # 智能穿戴泛类目录,编译为Entry类型的HAP包
    ├── default    # 默认设备泛类目录,编译为Entry类型的HAP包
    └── ...

部署模型A和部署模型B的主要差异点集中在products层。部署模型A在products目录下同一子目录中做功能和特性集成。部署模型B在products目录下不同子目录中对不同的产品做差异化的功能和特性集成。

二、界面级一多

2.1 界面级一多的核心问题

页面级一多需要考虑不同设备间的屏幕尺寸、色彩风格等存在差异,页面如何适配。可以从布局能力、资源使用、交互归一几个方面去考虑。

界面级一多的目标是让同一套UI代码在不同屏幕尺寸下都能呈现出良好的视觉效果和交互体验。需要从三个方面入手。

布局能力决定了页面中的元素按照何种方式排布及显示,是页面设计及开发过程中首先需要考虑的问题。一般情况下,可以通过页面或自定义组件内的组件结构,如组件个数、组件的父子/兄弟关系、组件类型、组件的相对位置,来判断使用何种布局能力。

对于随尺寸变化组件结构相同的场景,可以在开发过程中灵活使用自适应布局能力来达到目标效果。对于随尺寸变化组件结构不同的场景,更适合使用响应式布局能力来实现不同尺寸下的不同显示效果。

资源使用指在页面开发过程中,经常需要用到颜色、字体、间距、图片等资源,在不同的设备或配置中,这些资源的值可能不同。有两种处理方式。

交互归一指对于不同类型的智能设备,用户可能有不同的交互方式,如通过触摸屏、鼠标、触控板等。如果针对不同的交互方式单独做适配,会增加开发工作量同时产生大量重复代码。为解决这一问题,HarmonyOS统一了各种交互方式的API,即实现了交互归一。

2.2 自适应布局与响应式布局

布局可以分为自适应布局和响应式布局两种。

自适应布局是指当外部容器大小发生变化时,元素可以根据相对关系自动变化以适应外部容器变化的布局能力。相对关系如占比、固定宽高比、显示优先级等。自适应布局能力可以实现界面显示随外部容器大小连续变化。当前自适应布局能力有七种:拉伸能力、均分能力、占比能力、缩放能力、延伸能力、隐藏能力、折行能力。

响应式布局是指当外部容器大小发生变化时,元素可以根据断点、栅格或特定的特征,如屏幕方向、窗口宽高等,自动变化以适应外部容器变化的布局能力。当前响应式布局能力有三种:断点、媒体查询、栅格布局。响应式布局可以实现界面随外部容器大小有级不连续变化,通常不同特征下的界面显示会有较大的差异。

2.3 资源使用策略

在页面开发过程中,经常需要用到颜色、字体、间距、图片等资源,在不同的设备或配置中,这些资源的值可能不同。

处理方式一:应用资源。借助资源文件能力,开发者在应用中自定义资源,自行管理这些资源在不同的设备或配置中的表现。

处理方式二:系统资源。开发者直接使用系统预置的资源定义,即分层参数。

资源目录结构可以按设备类型、屏幕密度、语言等维度组织,实现资源的自动匹配。

三、自适应布局能力

3.1 自适应布局概述

针对常见的开发场景,方舟开发框架提炼了七种自适应布局能力。这些布局可以独立使用,也可多种布局叠加使用。

七种自适应布局能力包括拉伸能力、均分能力、占比能力、缩放能力、延伸能力、隐藏能力、折行能力。这些能力主要通过Flex布局、Row、Column等容器组件的属性来实现。

在鸿蒙开发中,实现多端页面布局常用的技术包括Row组件、Column组件和Flex组件。这三个组件提供了拉伸控制、占比控制、折行控制等自适应布局所需要的能力。

3.2 拉伸能力

拉伸能力是指容器组件尺寸发生变化时,增加或减小的空间全部分配给容器组件内指定区域。

实现拉伸能力的主要方式是Flex布局的flexGrow和flexShrink属性。

flexGrow用于设置组件在父容器主轴方向上的拉伸比例。当父容器有剩余空间时,设置了flexGrow属性的子组件会按比例分配剩余空间。

flexShrink用于设置组件在父容器主轴方向上的收缩比例。当父容器空间不足时,设置了flexShrink属性的子组件会按比例收缩。

Row() {
  Text('固定宽度')
    .width(100)
  Text('弹性拉伸')
    .flexGrow(1)
    .backgroundColor(Color.Gray)
}

3.3 均分能力

均分能力是指容器组件尺寸发生变化时,增加或减小的空间均匀分配给容器组件内所有空白区域。

实现均分能力的主要方式是Row组件、Column组件或Flex组件的justifyContent属性设置为FlexAlign.SpaceEvenly或FlexAlign.SpaceBetween。

SpaceEvenly表示Flex主轴方向均匀分配弹性元素,相邻元素之间的距离、第一个元素与行首的间距、最后一个元素到行尾的间距都完全一样。SpaceBetween表示Flex主轴方向均匀分配弹性元素,相邻元素之间距离相同,第一个元素与行首对齐,最后一个元素与行尾对齐。

Row() {
  Column() {
    Image($r('app.media.icon1'))
    Text('选项一')
  }
  Column() {
    Image($r('app.media.icon2'))
    Text('选项二')
  }
  Column() {
    Image($r('app.media.icon3'))
    Text('选项三')
  }
}
.width('100%')
.justifyContent(FlexAlign.SpaceEvenly)

3.4 占比能力

占比能力是指子组件的宽或高按照预设的比例,随容器组件发生变化。

实现占比能力有两种方式。第一种是将子组件的宽高设置为父组件宽高的百分比。第二种是使用layoutWeight属性。

layoutWeight属性用于设置兄弟组件在父容器主轴方向的布局权重。父容器尺寸确定时,设置了layoutWeight属性的子元素与兄弟元素占主轴尺寸按照权重进行分配,忽略元素本身尺寸设置,表示自适应占满剩余空间。

Column() {
  Text('标题区域')
    .height(50)
    .backgroundColor(Color.Red)
  Blank()
    .layoutWeight(1)
  Button('底部按钮')
    .height(60)
    .backgroundColor(Color.Blue)
}
.width('100%')
.height('100%')

3.5 缩放能力

缩放能力是指子组件的宽高按照预设的比例,随容器组件发生变化,且变化过程中子组件的宽高比不变。

实现缩放能力的主要方式是布局约束的aspectRatio属性。

aspectRatio用于设置组件的宽高比。当组件宽度变化时,高度会按比例自动调整。

Image($r('app.media.image'))
  .width('100%')
  .aspectRatio(1.5) // 宽高比为1.5:1

3.6 延伸能力

延伸能力是指容器组件内的子组件,按照其在列表中的先后顺序,随容器组件尺寸变化显示或隐藏。

实现延伸能力有两种方式。通过List组件实现,或通过Scroll组件配合Row组件或Column组件实现。

Scroll() {
  Row() {
    ForEach(this.items, (item: string) => {
      Text(item)
        .padding(10)
    })
  }
}
.scrollable(ScrollDirection.Horizontal)

3.7 隐藏能力

隐藏能力是指容器组件内的子组件,按照其预设的显示优先级,随容器组件尺寸变化显示或隐藏。相同显示优先级的子组件同时显示或隐藏。

实现隐藏能力的主要方式是布局约束的displayPriority属性。

Row() {
  Text('高优先级')
    .displayPriority(1)
  Text('中优先级')
    .displayPriority(2)
  Text('低优先级')
    .displayPriority(3)
}

3.8 折行能力

折行能力是指容器组件尺寸发生变化时,如果布局方向尺寸不足以显示完整内容,自动换行。

实现折行能力的主要方式是Flex组件的wrap属性设置为FlexWrap.Wrap。

Flex({
  direction: FlexDirection.Row,
  wrap: FlexWrap.Wrap
}) {
  ForEach(this.items, (item: string) => {
    Text(item)
      .padding(10)
      .margin(5)
      .backgroundColor(Color.Gray)
  })
}

四、响应式布局能力

4.1 响应式布局概述

响应式布局是指页面内的元素可以根据特定的特征,如窗口宽度、屏幕方向等,自动变化以适应外部容器变化的布局能力。

HarmonyOS为此提供了一系列的响应式布局能力和工具,用来实现多端布局。响应式布局是基于响应式设计方法论进行布局的方法,核心思想是页面根据不同屏幕尺寸自动调整布局,提供更舒适的界面和更好的用户体验。

响应式布局的能力包括断点、媒体查询、栅格布局,以及一些支持响应式布局的组件,如Tabs、Swiper、Grid、List、GridRow,通过断点设置可以实现不同的展示效果。

4.2 断点

响应式布局中最常使用的特征是窗口宽度及窗口高宽比,可以将窗口宽度及窗口高宽比划分为不同的范围,称之为断点。当窗口宽度及窗口高宽比从一个断点变化到另一个断点时,改变页面布局以获得更好的显示效果。

横向断点以应用窗口宽度为判断条件,建议划分为如下区间。

断点名称窗口宽度(vp)
xs(0, 320]
sm[320, 600)
md[600, 840)
lg[840, 1440)
xl[1440, +∞)

纵向断点根据应用窗口的高宽比进行判断,建议划分为如下区间。

断点名称高宽比
sm(0, 0.8)
md[0.8, 1.2)
lg[1.2, +∞)

断点面向窗口而非设备类型,相同断点区间的窗口展示相同的页面布局。同一设备上的不同窗口形态,例如全屏显示、分屏显示、自由窗口等,可能落入不同的断点区间,展示不同布局。

从API version 22起,开发者可利用响应式系统环境变量装饰器@Env读取断点信息。当组件所在窗口尺寸发生变化时,@Env装饰的断点环境变量将更新,并触发与该断点环境变量关联的组件刷新,从而实现界面内容的同步更新。

4.3 媒体查询

媒体查询支持监听窗口宽度、横竖屏、深浅色、设备类型等多种媒体特征,当媒体特征发生改变时同步调整页面布局。

let mediaQuery = matchMedia('(max-width: 600px)');
mediaQuery.on('change', (result: MediaQueryResult) => {
  if (result.matches) {
    // 小屏设备
  } else {
    // 大屏设备
  }
});

4.4 栅格布局

栅格组件将其所在的区域划分为有规律的多列,通过调整不同断点下的栅格组件的参数以及其子组件占据的列数等,实现不同的布局效果。

栅格布局的核心组件包括:

GridRow是栅格容器,定义布局的列数、间距和断点规则。GridCol是栅格子组件,控制子元素占据的列数、偏移和排序。断点系统根据设备屏幕宽度划分不同断点,动态切换布局规则。

GridRow({
  columns: 12,
  gutter: { x: 20, y: 10 },
  breakpoints: {
    value: ['320vp', '520vp', '840vp']
  }
}) {
  GridCol({
    span: { xs: 12, sm: 6, md: 4 },
    offset: { md: 1 }
  }) {
    Text('内容块')
  }
}

五、功能级一多

5.1 系统能力概述

应用开发至少包含两部分工作:UI页面开发和底层功能开发。功能级一多主要解决应用如何应对设备系统能力差异的兼容问题。

系统能力是HarmonyOS的核心概念。SysCap指操作系统中每一个相对独立的特性,如蓝牙、Wi-Fi、NFC、摄像头等,都是系统能力之一。每个系统能力对应多个API,随着目标设备是否支持该系统能力共同存在或消失。

与系统能力相关的有三个核心概念:

支持能力集是设备具备的系统能力集合,在设备配置文件中配置。要求能力集是应用需要的系统能力集合,在应用配置文件中配置。联想能力集是开发应用时IDE可联想的API所在的系统能力集合,在应用配置文件中配置。

只有当应用要求能力集是设备支持能力集的子集的时候,应用才可以在该设备上分发、安装和运行。

5.2 动态逻辑判断

如果某个系统能力没有写入应用的要求能力集中,那么在使用前需要判断设备是否支持该系统能力。

方法一:HarmonyOS定义了API canIUse帮助开发者来判断该设备是否支持某个特定的系统能力。

if (canIUse('SystemCapability.Communication.NFC.Core')) {
  console.log('该设备支持NFC能力');
} else {
  console.log('该设备不支持NFC能力');
}

方法二:开发者可通过import的方式将模块导入,若当前设备不支持该模块,import的结果为undefined,在使用其API时需判断是否存在。

import controller from '@kit.ConnectivityKit';
if (controller) {
  controller.enableNfc();
}

如果某系统能力是应用运行必须的,则要将其写入到应用的要求能力集中,以确保应用不会分发和安装到不符合要求的设备上。如果某系统能力不是应用运行必须的,则可以在运行时做动态判断,这样可以最大程度扩大应用的适用范围。

5.3 配置syscap.json

IDE会根据创建的工程所支持的设备自动配置联想能力集和要求能力集,同时也支持开发者修改。

在项目的src/main目录下创建syscap.json文件:

{
  "devices": {
    "general": [
      "default",
      "tablet"
    ],
    "custom": [
      {
        "某自定义设备": [
          "SystemCapability.Communication.SoftBus.Core"
        ]
      }
    ]
  },
  "development": {
    "addedSysCaps": [
      "SystemCapability.Communication.NFC.Core"
    ]
  },
  "production": {
    "addedSysCaps": [],
    "removedSysCaps": []
  }
}

对于要求能力集,开发者修改时要十分慎重,修改不当会导致应用无法分发和安装到目标设备上。对于联想能力集,通过增加系统能力可以扩大IDE可联想的API范围,但要注意这些API可能在某些设备上不支持,使用前需要判断。

六、工程级一多

6.1 应用程序包结构

HarmonyOS的应用以APP Pack形式发布,其包含一个或多个HAP包。HAP是HarmonyOS应用安装的基本单位,HAP可以分为Entry和Feature两种类型。

Entry类型的HAP是应用的主模块。在同一个应用中,同一设备类型只支持一个Entry类型的HAP,通常用于实现应用的入口界面、入口图标、主特性功能等。

Feature类型的HAP是应用的动态特性模块。Feature类型的HAP通常用于实现应用的特性功能,一个应用程序包可以包含一个或多个Feature类型的HAP,也可以不包含。

Module是HarmonyOS应用/服务的基本功能单元,包含了源代码、资源文件、第三方库及应用/服务配置文件,每一个Module都可以独立进行编译和运行。Module分为Ability和Library两种类型。Ability类型的Module编译后生成HAP包,Library类型的Module编译后生成HAR包或HSP包。

6.2 三层工程结构详解

一多推荐在应用开发过程中使用三层工程结构。这种分层架构通过HAP、HAR、HSP等模块化包类型在工程上实现。开发者可以根据功能是否需独立部署、是否动态加载等需求,灵活组合这些模块,最终编译出适应不同设备形态的应用包,实现一套代码,多端生成。

common层是公共能力层,存放公共基础能力集合,如工具库、公共配置等。该层追求稳定性和复用性,是应用开发的标准件仓库。common层可编译成一个或多个HAR包或HSP包,只可以被products和features依赖,不可以反向依赖。

features层是基础特性层,存放基础特性集合,如应用中相对独立的各个功能的UI及业务逻辑实现等。每个模块都应具备高内聚、低耦合的特性。它们依赖common层,并向上为products层提供清晰的业务接口。这一层的设计直接决定了多端部署的灵活性。features层可以横向调用及依赖common层。

products层是产品定制层,直面用户和设备差异。负责根据不同设备形态、交互方式和使用场景,对基础特性进行个性化的UI设计、资源适配和交互逻辑编排。products层各个子目录各自编译为一个Entry类型的HAP包,作为应用主入口。products层不可以横向调用。

6.3 不同部署模型的工程组织

部署模型A和部署模型B的主要差异点集中在products层。

部署模型A在products目录下同一子目录中做功能和特性集成。适用于相同泛类设备,UX设计或功能相似的情况。

部署模型B在products目录下不同子目录中对不同的产品做差异化的功能和特性集成。适用于不同泛类设备,UX设计或功能差异较大的情况。

七、一多实战案例分析

7.1 设置页面的自适应布局

以设置页面的cell视图为例,文字和图标在左侧,箭头图标在右侧。在不同尺寸的设备上运行时,始终保持左侧文字、右侧图标的效果。

布局结构分析显示,左侧为文字内容,右侧为图标,中间区域可以随着屏幕大小自动延伸。这里用到了延伸能力,需要使用Blank组件。

Blank组件是空白填充组件,在容器主轴方向上具有自动填充容器空余部分的能力,仅当父组件为Row、Column或Flex时生效。

Row() {
  Text('个人信息')
  Blank()
  Image($r('app.media.arrow'))
    .width(20)
    .height(20)
}
.width('90%')
.padding(10)

也可以使用Row组件的对齐方式来实现两端对齐效果,通过FlexAlign.SpaceBetween实现左右元素两端对齐。

7.2 多列内容的栅格布局

多列内容展示场景,如商品列表、图文混排等,适合使用栅格布局实现。

GridRow({
  columns: 12,
  gutter: { x: 20, y: 10 },
  breakpoints: {
    value: ['320vp', '600vp', '840vp']
  }
}) {
  ForEach(this.items, (item: Product) => {
    GridCol({
      span: { xs: 12, sm: 6, md: 4, lg: 3 }
    }) {
      ProductCard({ data: item })
    }
  })
}

7.3 不同泛类设备的差异化适配

对于手机和平板等不同泛类设备,可以采用products层的差异化工程组织方式。在products目录下分别创建phone和tablet子目录,各自编译为独立的Entry类型HAP包,根据设备类型选择不同的HAP组合部署。

八、一多开发最佳实践

8.1 布局设计原则

提升全场景体验,需考虑多设备连续性。应用页面布局设计时推荐遵循两个原则。

原则一:两个宽度相近的窗口,页面布局相同,断点归一。原则二:高度相对宽度较小的窗口,呈现横向窗口或类方形窗口时,页面布局进行差异化设计,增加断点。

系统设计了横向和纵向断点分别代表窗口的不同特征,作为判断页面布局和交互体验的条件。建议在实际开发中,根据应用实际使用场景决定适配哪些断点。

8.2 开发工具与调试

DevEco Studio作为官方IDE,多设备实时预览功能是一多开发的利器,可以同时查看同一界面在不同设备上的渲染效果。Codelinter支持对代码风格、安全、性能、一多适配等规则进行检查。

建议在开发过程中充分利用DevEco Studio提供的MultiPreview功能,配置多设备模拟器集群进行同步调试。

8.3 性能优化建议

一多开发需要考虑不同设备的性能差异。对于低性能设备,应适当降低UI复杂度,减少动画效果,优化图片资源。

使用组件复用机制,减少滑动过程中组件创建和布局开销,提升帧率。合理使用布局边界,限制布局影响范围,减少重新布局的计算量。

8.4 测试策略

一多应用需要在多种设备上进行全面测试。可以使用自动化测试框架支持多设备并行测试。建议覆盖不同屏幕尺寸、不同系统能力组合、不同交互方式的测试场景。

UXLint可以检查多端适配规范,帮助发现布局适配、资源使用等方面的问题。

总结

本文系统讲解了一次开发多端部署的完整技术方案。从一多的设计理念出发,深入解析了界面级一多的自适应布局和响应式布局能力,功能级一多的系统能力兼容机制,以及工程级一多的三层工程结构和部署模型。

通过本文的学习,希望你能够理解一多的核心定义和目标,掌握七种自适应布局能力和三种响应式布局能力的使用方法,理解系统能力的概念和动态判断方法,掌握三层工程结构的组织方式,了解不同部署模型的选择策略。

一多是HarmonyOS面向多终端的核心设计理念,掌握一多开发是开发鸿蒙跨设备应用的关键。第五篇将深入讲解数据管理与持久化,包括应用数据存储概述、文件与用户数据、分布式数据管理等核心内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码丨神

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值