Android Gradle使用笔记

目录

1. 配置构建

1.1 构建流程

这里写图片描述

1.2 自定义构建配置

可以自定义的构建配置:

配置名称
buildType(构建类型)构建类型定义 Gradle 在构建和打包您的应用时使用的某些属性,通常针对开发生命周期的不同阶段进行配置。例如,调试构建类型支持调试选项,使用调试密钥签署 APK;而发布构建类型则可压缩、混淆 APK 以及使用发布密钥签署 APK 进行分发。您必须至少定义一个构建类型才能构建应用 - Android Studio 默认情况下会创建调试和发布构建类型。
productFlavors(产品风味)产品风味代表您可以发布给用户的不同应用版本,例如免费和付费的应用版本。您可以将产品风味自定义为使用不同的代码和资源,同时对所有应用版本共有的部分加以共享和重复利用。产品风味是可选项,并且您必须手动创建。
BuildVariants(构建变体)构建变体是构建类型与产品风味的交叉产物,是 Gradle 在构建应用时使用的配置
Manifest Entries(清单条目)您可以为构建变体配置中清单文件的一些属性指定值。这些构建值会替换清单文件中的现有值。
Dependencies(依赖)构建系统管理来自您的本地文件系统以及来自远程存储区的项目依赖项。
Signing(签名)构建系统让您能够在构建配置中指定签署设置,并可在构建过程中自动签署您的 APK。
ProGuard(混淆)构建系统让您能够为每个构建变体指定不同的 ProGuard 规则文件。构建系统可在构建过程中运行 ProGuard 对类进行压缩和混淆处理。
APK 拆分构建系统让您能够自动构建不同的 APK,并且每个 APK 只包含特定屏幕密度或应用二进制界面 (ABI) 所需的代码和资源。

1.3 构建配置文件

这里写图片描述

1.3.1 Gradle设置文件(setting.gradle)

位于项目根目录,用于指示 Gradle 在构建应用时应将哪些模块包括在内。
include ':app', 'library'

1.3.2 顶级构建文件

项目根目录的build.gradle,用于定义适用于项目中所有模块的构建配置。

/**
 *  buildscript {} 代码块来定义项目中所有模块共用的 Gradle 存储区和依赖项。
 *  配置gradle的仓库与依赖,不能导入module的依赖
 *  此处可以为android 的gradle插件用于构建modules
 */

buildscript {

    /**
     * repositories {} 配置gradle用于下载和查询依赖的库
     * gradle支持远程仓库比如JCenter, Maven Central, and Ivy
     * 也可是使用本地库或者定义自己的远程库
     */

    repositories {
        jcenter()
    }

    /**
     * dependencies {}配置gradle用于构建项目的依赖
     */

    dependencies {
        //配置了Android Gradle 3.0.1插件
        classpath 'com.android.tools.build:gradle:3.0.1'
    }
}

/**
 * allprojects {} 配置所有modules会用到的仓库或者依赖,比如第三方插件或者库。
 * 不是所有module都会用到的库,应在配置在module-level
 * Android Studio 把JCenter作为默认库,但是并未配置任何依赖
 */

allprojects {
   repositories {
       jcenter()
   }
}

1.3.3 项目级构建文件

模块级 build.gradle 文件位于每个 <project>/<module>/ 目录,用于配置适用于其所在模块的构建设置。可以通过配置这些构建设置来提供自定义打包选项(例如附加构建类型和产品风味),以及替换 main/ 应用清单或顶级 build.gradle 文件中的设置。

/**
 * 第一行应用了Android 的 Gradle 插件,使android {}块可用于指定Android特定的构建选项。
 */

apply plugin: 'com.android.application'

/**
 * android {} 配置android特定的构建选项
 */

android {

  /**
   * compileSdkVersion 指定了Gradle用来编译的Android API版本,
   * 意味着App可以使用在此版本和低于此版本的API
   * 
   * buildToolsVersion 指定了SDK 构建工具,命令行,编译器的版本
   */

  compileSdkVersion 26
  buildToolsVersion "26.0.2"

  /**
   * defaultConfig {}封装所有的默认设置和条目构建变体
   * 并且可以动态从构建系统覆盖main / AndroidManifest.xml中的一些属性
   * 您可以通过修改这些值来配置产品风格
   */

  defaultConfig {

    /**
     * applicationId 发布的唯一标识
     * 应该引用在main/AndroidManifest.xml中定义的包名
     */

    applicationId 'com.example.myapp'

    // 定义运行App的最小API级别
    minSdkVersion 15

    //指定了用于测试App的API级别
    targetSdkVersion 26

    //定义app版本号
    versionCode 1

    //定义版本名称
    versionName "1.0"
  }

  /**
   * 默认为两种 debug和release
   * debug没有明确显示在配置中,但是它包含测试工具并且使用调试秘钥进行签名,
   * release构建类型需要通过Proguard设置,默认不会被签名
   */

  buildTypes {

    /**
     * 默认情况下,Android Studio配置发布构建类型以启用代码压缩,使用minifyEnabled,
     * 并指定Proguard设置文件。
     */

    release {
        minifyEnabled true //允许代码压缩
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }

  /**
   * productFlavors {} 可以配置多种产品风味,
   * 可以创建不同版本的应用程序,并使用设置的 defaultConfig{}。
   * product Flavors 是可选的,默认不会创建
   * 每种产品风味都会指定自己的应用程序ID,以便它们可以同时存在于GooglePlay商店或Android设备上
   */

  productFlavors {
    free {
      applicationId 'com.example.myapp.free'
    }

    paid {
      applicationId 'com.example.myapp.paid'
    }
  }

  /**
   * splits{} 可以配置不同APK版本,每个APK版本只包含支持的屏幕密度或ABI的代码资源。
   * 还需要配置构建,以便于每个APK都具有不同的versionCode
   */

  splits {
    // 屏幕分辨率分割设置
    density {

      // 启用或禁用
      enable false

      // 排除这些密度
      exclude "ldpi", "tvdpi", "xxxhdpi", "400dpi", "560dpi"
    }
  }
}

/**
 * dependencies {} 只指定module本事需要的依赖
 */

dependencies {
    compile project(":lib")
    compile 'com.android.support:appcompat-v7:27.0.2'
    compile fileTree(dir: 'libs', include: ['*.jar'])
}

1.3.4 Gradle属性文件

Gradle 还包括两个属性文件,位于项目根目录,可用于指定适用于 Gradle 构建工具包本身的设置:

  • gradle.properties
    您可以在其中配置项目范围 Gradle 设置,例如 Gradle 后台进程的最大堆大小。如需了解详细信息,请参阅构建环境。

  • local.properties
    为构建系统配置本地环境属性,例如 SDK 安装路径。由于该文件的内容由 Android Studio 自动生成并且专用于本地开发者环境,因此您不应手动修改该文件,或将其纳入您的版本控制系统。

1.3.5 源集

Android Studio 按逻辑关系将每个模块的源代码和资源分组为源集。模块的 main/ 源集包括其所有构建变体共用的代码和资源。其他源集目录为可选项,在您配置新的构建变体时,Android Studio 不会自动为您创建这些目录,需要手动创建。

  • src/main/
    此源集包括所有构建变体共用的代码和资源。
  • src/<buildType>/
    创建此源集可加入特定构建类型专用的代码和资源。
  • src/<productFlavor>/
    创建此源集可加入特定产品风味专用的代码和资源。
  • src/<productFlavorBuildType>/
    创建此源集可加入特定构建变体专用的代码和资源。

  • EG 例如,要生成应用的“完整调试”版本,构建系统需要合并来自以下源集的代码、设置和资源:

    • src/fullDebug/(构建变体源集)
    • src/debug/(构建类型源集)
    • src/full/(产品风味源集)
    • src/main/(主源集)

如果不同源集包含同一文件的不同版本,Gradle 将按以下优先顺序决定使用哪一个文件(左侧源集替换右侧源集的文件和设置):

构建变体 > 构建类型 > 产品风味 > 主源集 > 库依赖项

2. 配置构建变体

每个构建变体都代表可以为应用构建的一个不同版本

构建变体是 Gradle 按照特定规则集合并在构建类型和产品风味中配置的设置、代码和资源所生成的结果。您并不直接配置构建变体,而是配置组成变体的构建类型和产品风味。

2.1 配置构建类型(buildTypes)

可以在模块级 build.gradle 文件的 android {} 代码块内部创建和配置构建类型。 当您创建新模块时,Android Studio 会自动为您创建调试和发布这两种构建类型。尽管调试构建类型不会出现在构建配置文件中,Android Studio 会为其配置 debuggable true。这样,您可以在安全的 Android 设备上调试应用并使用通用调试密钥库配置 APK 签署。

android {
    ...
    defaultConfig {...}
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }

        debug {
                // 指定applicationId的后缀名
            applicationIdSuffix ".debug"
        }

        /**
         * initWith 从其他构建类型复制配置
         */

        jnidebug {

            // 复制debug的配置
            initWith debug

            applicationIdSuffix ".jnidebug"
            jniDebuggable true
        }
    }
}

2.2 配置产品风味(productFlavors)

在productFlavors中添加配置,支持与defaultConfig相同的属性。

android {
    ...
    defaultConfig {...}
    buildTypes {...}
    productFlavors {
        demo {
            applicationIdSuffix ".demo"
            versionNameSuffix "-demo"
        }
        full {
            applicationIdSuffix ".full"
            versionNameSuffix "-full"
        }
    }
}

2.2.1 组合多个产品风味
android {
  ...
  buildTypes {
    debug {...}
    release {...}
  }

  // Specifies the flavor dimensions you want to use. The order in which you
  // list each dimension determines its priority, from highest to lowest,
  // when Gradle merges variant sources and configurations. You must assign
  // each product flavor you configure to one of the flavor dimensions.
  flavorDimensions "api", "mode"

  productFlavors {
    demo {
      // Assigns this product flavor to the "mode" flavor dimension.
      dimension "mode"
      ...
    }

    full {
      dimension "mode"
      ...
    }

    // Configurations in the "api" product flavors override those in "mode"
    // flavors and the defaultConfig {} block. Gradle determines the priority
    // between flavor dimensions based on the order in which they appear next
    // to the flavorDimensions property above--the first dimension has a higher
    // priority than the second, and so on.
    minApi24 {
      dimension "api"
      minSdkVersion '24'
      // To ensure the target device receives the version of the app with
      // the highest compatible API level, assign version codes in increasing
      // value with API level. To learn more about assigning version codes to
      // support app updates and uploading to Google Play, read Multiple APK Support
      versionCode 30000 + android.defaultConfig.versionCode
      versionNameSuffix "-minApi24"
      ...
    }

    minApi23 {
      dimension "api"
      minSdkVersion '23'
      versionCode 20000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi23"
      ...
    }

    minApi21 {
      dimension "api"
      minSdkVersion '21'
      versionCode 10000  + android.defaultConfig.versionCode
      versionNameSuffix "-minApi21"
      ...
    }
  }
}
...

Gradle 可以使用以下命名方案创建总共 12 个构建变体:

构建变体:[minApi24, minApi23, minApi21][Demo, Full][Debug, Release]

对应 APK:app-[minApi24, minApi23, minApi21]-[demo, full]-[debug, release].apk

例如,
构建变体:minApi24DemoDebug
对应 APK:app-minApi24-demo-debug.apk

2.2.2 过滤变体
android {
  ...
  buildTypes {...}

  flavorDimensions "api", "mode"
  productFlavors {
    demo {...}
    full {...}
    minApi24 {...}
    minApi23 {...}
    minApi21 {...}
  }

  variantFilter { variant ->
      def names = variant.flavors*.name
      // To check for a certain build type, use variant.buildType.name == "<buildType>"
      if (names.contains("minApi21") && names.contains("demo")) {
          // Gradle ignores any variants that satisfy the conditions above.
          setIgnore(true)
      }
  }
}
...

2.3 创建用于构建变体的源集


------------------------------------------------------------
Project :app
------------------------------------------------------------

...

debug
----
Compile configuration: compile
build.gradle name: android.sourceSets.debug
Java sources: [app/src/debug/java]
Manifest file: app/src/debug/AndroidManifest.xml
Android resources: [app/src/debug/res]
Assets: [app/src/debug/assets]
AIDL sources: [app/src/debug/aidl]
RenderScript sources: [app/src/debug/rs]
JNI sources: [app/src/debug/jni]
JNI libraries: [app/src/debug/jniLibs]
Java-style resources: [app/src/debug/resources]

2.3.1 更改默认源集配置
android {
  ...
  sourceSets {
    // Encapsulates configurations for the main source set.
    main {
      // Changes the directory for Java sources. The default directory is
      // 'src/main/java'.
      java.srcDirs = ['other/java']

      // If you list multiple directories, Gradle uses all of them to collect
      // sources. Because Gradle gives these directories equal priority, if
      // you define the same resource in more than one directory, you get an
      // error when merging resources. The default directory is 'src/main/res'.
      res.srcDirs = ['other/res1', 'other/res2']

      // Note: You should avoid specifying a directory which is a parent to one
      // or more other directories you specify. For example, avoid the following:
      // res.srcDirs = ['other/res1', 'other/res1/layouts', 'other/res1/strings']
      // You should specify either only the root 'other/res1' directory, or only the
      // nested 'other/res1/layouts' and 'other/res1/strings' directories.

      // For each source set, you can specify only one Android manifest.
      // By default, Android Studio creates a manifest for your main source
      // set in the src/main/ directory.
      manifest.srcFile 'other/AndroidManifest.xml'
      ...
    }

    // Create additional blocks to configure other source sets.
    androidTest {

      // If all the files for a source set are located under a single root
      // directory, you can specify that directory using the setRoot property.
      // When gathering sources for the source set, Gradle looks only in locations
      // relative to the root directory you specify. For example, after applying the
      // configuration below for the androidTest source set, Gradle looks for Java
      // sources only in the src/tests/java/ directory.
      setRoot 'src/tests'
      ...
    }
  }
}
...

2.3.2 使用源集构建

您可以使用源集目录包含您希望仅针对某些配置打包的代码和资源。例如,如果您要构建“演示调试”构建变体(它是“演示”产品风味和“调试”构建类型的合体),Gradle 会查看这些目录并赋予以下优先级顺序:

  • src/demoDebug/(构建变体源集)
  • src/debug/(构建类型源集)
  • src/demo/(产品风味源集)
  • src/main/(主源集)

2.4 声明依赖

android {...}
...
dependencies {
    // The 'compile' configuration tells Gradle to add the dependency to the
    // compilation classpath and include it in the final package.

    // Dependency on the "mylibrary" module from this project
    compile project(":mylibrary")

    // Remote binary dependency
    compile 'com.android.support:appcompat-v7:27.0.2'

    // Local binary dependency
    compile fileTree(dir: 'libs', include: ['*.jar'])
}
  • 模块依赖项

    compile project(':mylibrary') 行声明了一个名为“mylibrary”的本地 Android 库模块作为依赖项,
    并要求构建系统在构建应用时编译并包含该本地模块。
  • 远程二进制依赖

    compile 'com.android.support:appcompat-v7:27.0.2' 行
    会通过指定其 JCenter 坐标,针对 Android 支持库的 27.0.2 版本声明一个依赖项。
    默认情况下,Android Studio 会将项目配置为使用顶级构建文件中的 JCenter 存储区。
    当您将项目与构建配置文件同步时,Gradle 会自动从 JCenter 中抽取依赖项。或者,您也可以通过使用 SDK 管理器下载和安装特定的依赖项。
  • 本地二进制依赖项

    compile fileTree(dir: 'libs', include: ['*.jar']) 行
    告诉构建系统在编译类路径和最终的应用软件包中包含 app/libs/ 
    目录内的任何 JAR 文件。如果您有模块需要本地二进制依赖项,
    请将这些依赖项的 JAR 文件复制到项目内部的 <moduleName>/libs 中

2.4.1 配置依赖项
  • compile
    指定编译时依赖项。Gradle 将此配置的依赖项添加到类路径和应用的 APK。这是默认配置。
  • apk
    指定 Gradle 需要将其与应用的 APK 一起打包的仅运行时依赖项。您可以将此配置与 JAR 二进制依赖项一起使用,而不能与其他库模块依赖项或 AAR 二进制依赖项一起使用。
  • provided
    指定 Gradle 不与应用的 APK 一起打包的编译时依赖项。如果运行时无需此依赖项,这将有助于缩减 APK 的大小。您可以将此配置与 JAR 二进制依赖项一起使用,而不能与其他库模块依赖项或 AAR 二进制依赖项一起使用。

通过构建变体指定依赖项:

dependencies {
    ...
    // Adds specific library module dependencies as compile time dependencies
    // to the fullRelease and fullDebug build variants.
    fullReleaseCompile project(path: ':library', configuration: 'release')
    fullDebugCompile project(path: ':library', configuration: 'debug')

    // Adds a compile time dependency for local tests.
    testCompile 'junit:junit:4.12'

    // Adds a compile time dependency for the test APK.
    androidTestCompile 'com.android.support.test.espresso:espresso-core:2.2.2'
}

2.5 配置签署设置

...
android {
    ...
    defaultConfig {...}
    signingConfigs {
        release {
            storeFile file("myreleasekey.keystore")
            storePassword "password"
            keyAlias "MyReleaseKey"
            keyPassword "password"
        }
    }
    buildTypes {
        release {
            ...
            signingConfig signingConfigs.release
        }
    }
}
  • 通过环境变量获取这些密码:

    storePassword System.getenv("KSTOREPWD")
    keyPassword System.getenv("KEYPWD")
  • 要让构建流程在您要从命令行调用此构建时提示您输入这些密码:

    storePassword System.console().readLine("\nKeystore password: ")
    keyPassword System.console().readLine("\nKey password: ")

3. APK拆分

多个包含特定屏幕密度或ABI文件的APK,是减少APK大小的一种方法

3.1 为多个APK配置构建

通过在module-level的build.gradle文件中添加,splits{},配置APK拆分。在splits{}中提供了density{}来指定每一种屏幕的APK,在abi{}中提供了每一中ABI的APK

3.1.1 为屏幕密度配置多个APK

需要在splits{}中添加density{},在density中提供所需的屏幕密度和尺寸。这个兼容屏幕大小的列表只有在manifest中需要指定使用。以下操作用来指定屏幕密度

  • enable
    是否创建基于屏幕的密度的APK
  • exclude
    指定一个以逗号分隔的Gradle列表,Gradle不应该为其生成单独的APK。如果要为大多数密度生成APK,请使用exclude,但需要排除应用程序不支持的一些密度。
  • reset()
    清除默认列表(必须和include连用)

    reset()  // Clears the default list from all densities to no densities.
    include "ldpi", "xxhdpi" // Specifies the two densities we want to generate APKs for.
  • include
    指定Gradle应为其生成APK的逗号分隔密度列表。
  • compatibleScreens
    指定兼容屏幕大小的逗号分隔列表。 这会为每个APK在清单中注入一个匹配的节点。
android {
  ...
  splits {

    //配置基于屏幕密度的APK
    density {

      //配置是否要创建基于屏幕密度的APK
      enable true

      //排除指定的屏幕密度
      exclude "ldpi", "xxhdpi", "xxxhdpi"

      // Specifies a list of compatible screen size settings for the manifest.
      compatibleScreens 'small', 'normal', 'large', 'xlarge'
    }
  }
}

3.1.2 为ABI配置多个APK
  • enable
    默认为false,若为True则会生成基于ABI的多个APK
  • exclude
    指定排除的ABI
  • reset()
    清除列表必须与include 连用

    reset()  
    include "x86", "mips" 
  • include
    指定要生成的对应ABI的APK
  • universalApk
    若为true,则生成一个通用的APK,以及每个ABI的APK。默认值为false。挡根据屏幕密度构建时,Gradle总是只生成一个通用的APK
android {
  ...
  splits {

    // Configures multiple APKs based on ABI.
    abi {

      // Enables building multiple APKs per ABI.
      enable true

      // By default all ABIs are included, so use reset() and include to specify that we only
      // want APKs for x86, armeabi-v7a, and mips.

      // Resets the list of ABIs that Gradle should create APKs for to none.
      reset()

      // Specifies a list of ABIs that Gradle should create APKs for.
      include "x86", "armeabi-v7a", "mips"

      // Specifies that we do not want to also generate a universal APK that includes all ABIs.
      universalApk false
    }
  }
}

3.1.3 配置版本

android {
  ...
  defaultConfig {
    ...
    versionCode 4
  }
  splits {
    ...
  }
}

// Map for the version code that gives each ABI a value.
ext.abiCodes = ['armeabi-v7a':1, mips:2, x86:3]

// For per-density APKs, create a similar map like this:
// ext.densityCodes = ['mdpi': 1, 'hdpi': 2, 'xhdpi': 3]

import com.android.build.OutputFile

// For each APK output variant, override versionCode with a combination of
// ext.abiCodes * 1000 + variant.versionCode. In this example, variant.versionCode
// is equal to defaultConfig.versionCode. If you configure product flavors that
// define their own versionCode, variant.versionCode uses that value instead.
android.applicationVariants.all { variant ->

  // Assigns a different version code for each output APK
  // other than the universal APK.
  variant.outputs.each { output ->

    // Stores the value of ext.abiCodes that is associated with the ABI for this variant.
    def baseAbiVersionCode =
            // Determines the ABI for this variant and returns the mapped value.
            project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

    // Because abiCodes.get() returns null for ABIs that are not mapped by ext.abiCodes,
    // the following code does not override the version code for universal APKs.
    // However, because we want universal APKs to have the lowest version code,
    // this outcome is desirable.
    if (baseAbiVersionCode != null) {

      // Assigns the new version code to versionCodeOverride, which changes the version code
      // for only the output APK, not for the variant itself. Skipping this step simply
      // causes Gradle to use the value of variant.versionCode for the APK.
      output.versionCodeOverride =
              baseAbiVersionCode * 1000 + variant.versionCode
    }
  }
}

3.2 构建多个APK

...
  splits {
    density {
      enable true
      reset()
      include "mdpi", "hdpi"
    }
    abi {
      enable true
      reset()
      include "x86", "mips"
    }
  }

生成结果:

  • app-hdpiX86-release.apk: Contains code and resources for hdpi density and x86 ABI only.
  • app-hdpiMips-release.apk: Contains code and resources for hdpi density and mips ABI only.
  • app-mdpiX86-release.apk: Contains code and resources for mdpi density and x86 ABI only.
  • app-mdpiMips-release.apk: Contains code and resources for mdpi density and mips ABI only.

3.2.1 APK名称格式

modulename-screendensityABI-buildvariant.apk

4. 64K方法数问题

单个 Dalvik Executable (DEX) 字节码文件内的代码可调用的引用总数为64k(65536)。

4.1 规避64K

  • 检查应用的直接和传递依赖项
    确保您在应用中使用任何庞大依赖库所带来的好处大于为应用添加大量代码所带来的弊端。
  • 通过ProGUard移除未使用的代码
    启用代码压缩以运行Proguard

4.2 5.0以上版本解决

android {
    defaultConfig {
        ...
        minSdkVersion 21 
        targetSdkVersion 26
        multiDexEnabled true
    }
    ...
}

4.3 5.0以下版本解决

  • 启用支持可执行文件分包,并将Dalvik可执行文件分包作为依赖
android {
    defaultConfig {
        ...
        minSdkVersion 15 
        targetSdkVersion 26
        multiDexEnabled true
    }
    ...
}

dependencies {
  compile 'com.android.support:multidex:1.0.1'
}
  • 根据是否要替换Application类,执行以下操作:

    • 若未替换Application

      <?xml version="1.0" encoding="utf-8"?>
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.myapp">
          <application
                  android:name="android.support.multidex.MultiDexApplication" >
              ...
          </application>
      </manifest> 
    • 若替换了Application,使Application扩展自MultiApplication

      public class MyApplication extends MultiDexApplication { ... }
    • 如果您替换了 Application 类,但无法更改基本类,则可以改为替换 attachBaseContext() 方法并调用 MultiDex.install(this) 来启用 Dalvik 可执行文件分包:

      public class MyApplication extends SomeOtherApplication {
        @Override
        protected void attachBaseContext(Context base) {
           super.attachBaseContext(context);
           Multidex.install(this);
        }
      }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值