Android 中的 Gradle 配置介紹

目錄

Android Studio 目錄層級(jí)

├── app #Android App目錄
│   ├── app.iml
│   ├── build #構(gòu)建輸出目錄
│   ├── build.gradle #構(gòu)建腳本
│   ├── libs #so相關(guān)庫
│   ├── proguard-rules.pro #proguard混淆配置
│   └── src #源代碼,資源等
├── build
│   └── intermediates
├── build.gradle #工程構(gòu)建文件
├── gradle
│   └── wrapper
├── gradle.properties #gradle的配置 外部屬性
├── gradlew #gradle wrapper linux shell腳本
├── gradlew.bat
├── LibSqlite.iml
├── local.properties #配置Androod SDK位置文件
└── settings.gradle #工程配置

settings.gradle

settings.gradle 用于配置 project。settings 文件聲明了所需的配置來實(shí)例化項(xiàng)目的層次結(jié)構(gòu),標(biāo)明其下有幾個(gè) module,比如這里包含一個(gè):appmodule。

include ':app'

根目錄的 build.gradle

settings.gradle 在同一目錄下的 build.gradle 是一個(gè)頂級(jí)的 build 配置文件,在這里可以為所有的 project 以及 module 配置一些常用的配置。

根目錄中的 build.gradle 默認(rèn)包含 2 個(gè)代碼塊

  • buildscript{} 用于配置構(gòu)建腳本所用到的代碼庫和依賴關(guān)系。這里定義了 Android 編譯工具的類路徑。repositories 中, jCenter 是一個(gè)著名的 Maven 倉庫。
  • allprojects{} 用于定義所有模塊需要用到的一些公共屬性。這里定義的屬性會(huì)被應(yīng)用到所有的 module 中,但為了保證每個(gè)項(xiàng)目的獨(dú)立性,一般不會(huì)在這里操作太多共有的東西
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()//使用 jcenter庫
    }
    dependencies {
        // 依賴 android提供的 1.1.0 的 gradle build
        classpath 'com.android.tools.build:gradle:1.1.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
//為所有的工程的 repositories 配置為 jcenters
allprojects {
    repositories {
        jcenter()
    }
}

模塊級(jí) build.gradle

除了更目錄下的 build 文件,gradle 支持對每個(gè) module 進(jìn)行配置,主要有三個(gè)重要的代碼塊:plugin、android 和 dependencies

apply plugin: 'com.android.application'

android {
    compileSdkVersion 21
    buildToolsVersion "22.0.1"

    defaultConfig {
        applicationId "org.flysnow.demo"
        minSdkVersion 9
        targetSdkVersion 21
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

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

apply plugin

第一行 apply plugin: 'com.android.application',這表示該 module 是一個(gè) app module,應(yīng)用了com.android.application 插件。
如果是一個(gè) android library,那么這里的是 apply plugin: 'com.android.library'

  • apply plugin:聲明引用插件的類型。如果是庫的話就加
  • apply from:表示引用其他的配置文件,比如 apply from:"config.gradle"

android

compileSdkVersion & buildToolsVersion

  • compileSdkVersion: 基于哪個(gè) SDK 編譯,這里的 Version 是 API LEVEL,是21
  • buildToolsVersion: 基于哪個(gè)構(gòu)建工具版本進(jìn)行構(gòu)建的

defaultConfig

defaultConfig 是默認(rèn)配置,如果沒有其他的配置覆蓋,就會(huì)使用這里的。

常用屬性:

  • applicationId 配置包名的

  • versionCode 版本號(hào)

  • versionName 版本名稱

  • minSdkVersion app能夠運(yùn)行的最小版本

  • targetSdkVersion 目標(biāo)設(shè)備sdk(向前兼容)

    一般 minSdkVersion < targetSdkVersion <= compileSdkVersion
    miniSdkVersion: app能運(yùn)行的最小版本,比如 minSdkVersion=18(Android 4.3)那么在低于這個(gè)系統(tǒng)版本的設(shè)備上就會(huì)提示不能安裝或運(yùn)行。

    compileSdkVersion: 是我們告訴 Gradle,我們是用哪一版本的Android Sdk去編譯程序的,可以使用這個(gè)版本的 API,比如我們使用的是 7.0 的版本
    compileSdkVersion=24,那么我們對于拍照裁剪圖片等功能的操作,就可以使用 FileProvider了。

    targetSdkVersion: 系統(tǒng)通過 targetSdkVersion 來保證 Android 的向前兼容性,在 Android4.4 之后的設(shè)備上,系統(tǒng)會(huì)判斷你的 targetSdkVersion 是否小于 19,如果小于的話,那就按照 19 之前的 api 方法,如果大于等于 19,那么就按照之后的 api 方法來走,保證了程序運(yùn)行的一致性。也就是向前兼容性。

defaultConfig{} 還有很多其他的配置,后面介紹

buildTypes

buildTypes 是構(gòu)建類型,常用的有 releasedebug 兩種,可以在這里面啟用混淆,啟用 zipAlign 以及配置簽名信息等。

buildTypes{} 中的其他配置,后面介紹

dependencies

dependencies 就不屬于 Android 專有的配置了,它定義了該 module 需要依賴的 jar,aar,jcenter庫信息。

apply plugin: 'com.android.application'

android { ... }

dependencies {
    // Dependency on a local library module
    implementation project(":mylibrary")

    // Dependency on local binaries
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    // Dependency on a remote binary
    implementation 'com.example.android:app-magic:12.3'
}

本地項(xiàng)目依賴

implementation project(':mylibrary')

這聲明了對一個(gè)名為“mylibrary”(此名稱必須與在您的 settings.gradle 文件中使用 include: 定義的庫名稱相符)的 Android 庫模塊的依賴關(guān)系。在構(gòu)建您的應(yīng)用時(shí),構(gòu)建系統(tǒng)會(huì)編譯該庫模塊,并將生成的編譯內(nèi)容打包到 APK 中。

本地二進(jìn)制文件依賴項(xiàng)

implementation fileTree(dir: 'libs', include: ['*.jar'])

Gradle 聲明了對項(xiàng)目的 module_name/libs/ 目錄中 JAR 文件的依賴關(guān)系(因?yàn)?Gradle 會(huì)讀取 build.gradle 文件的相對路徑)。

或者,您也可以按如下方式指定各個(gè)文件:

implementation files('libs/foo.jar', 'libs/bar.jar')

遠(yuǎn)程二進(jìn)制文件依賴項(xiàng)

implementation 'com.example.android:app-magic:12.3'

這實(shí)際上是以下代碼的簡寫形式:

implementation group: 'com.example.android', name: 'app-magic', version: '12.3'

這聲明了對“com.example.android”命名空間組內(nèi)的 12.3 版“app-magic”庫的依賴關(guān)系。

注意:此類遠(yuǎn)程依賴項(xiàng)要求您聲明適當(dāng)?shù)?a target="_blank">遠(yuǎn)程代碼庫,Gradle 應(yīng)在其中查找相應(yīng)的庫。如果本地不存在相應(yīng)的庫,那么當(dāng) build 需要它時(shí)(例如,當(dāng)您點(diǎn)擊 Sync Project with Gradle Files 圖標(biāo) [站外圖片上傳中...(image-aa6d99-1607933369919)] 或運(yùn)行 build 時(shí)),Gradle 會(huì)從遠(yuǎn)程站點(diǎn)提取它。

依賴項(xiàng)配置

  • implementation: Gradle 會(huì)將依賴項(xiàng)添加到編譯類路徑和構(gòu)建輸出,依賴不會(huì)暴露給其他模塊
  • api:Gradle 會(huì)將依賴項(xiàng)添加到編譯類路徑和構(gòu)建輸出,依賴會(huì)傳遞給其他依賴該模塊的模塊
  • compileOnly:Gradle 只會(huì)將依賴項(xiàng)添加到編譯類路徑,僅在編譯期依賴,運(yùn)行時(shí)可有可無時(shí)可以使用該配置
  • runtimeOnly:Gradle 只會(huì)將依賴項(xiàng)添加到構(gòu)建輸出,以便在運(yùn)行時(shí)使用。也就是說,不會(huì)將其添加到編譯類路徑。
    annotationProcessor:添加注解處理器的庫依賴關(guān)系,需使用該配置將其添加到注解處理器類路徑。注:Kotlin 使用 kapt 聲明注解處理器依賴項(xiàng)

以上配置可以將依賴項(xiàng)應(yīng)用于所有構(gòu)建變體

如果想為特定的構(gòu)建變體源代碼集或測試源代碼集聲明依賴,必須將配置的名稱首字母大寫,并在前面加上構(gòu)建變體或測試源代碼集的名稱作為前綴

dependencies {
    freeImplementation 'com.google.firebase:firebase-ads:9.8.0'
}

不過,如果您想為將產(chǎn)品變種和構(gòu)建類型組合在一起的變體添加依賴項(xiàng),就必須在 configurations 代碼塊中初始化配置名稱。以下示例向“freeDebug”構(gòu)建變體添加了 runtimeOnly 依賴項(xiàng)(使用本地二進(jìn)制文件依賴項(xiàng)):

configurations {
    // Initializes a placeholder for the freeDebugRuntimeOnly dependency
    // configuration.
    freeDebugRuntimeOnly {}
}

dependencies {
    freeDebugRuntimeOnly fileTree(dir: 'libs', include: ['*.jar'])
}

ext{} 代碼塊 定制項(xiàng)目屬性

在根目錄 build.gradle 中, 可以定制適用于所有模塊的屬性,通過 ext 代碼塊來實(shí)現(xiàn),如:

ext {
    compileSdkVersion = 28
    minSdkVersion = 18
}

然后在模塊的 build.gradle 配置引用這些屬性,語法為:
rootProject.ext.{屬性名}, 比如:

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
} 

構(gòu)建類型 android.buildTypes{}

默認(rèn)情況下,Android Plugin 會(huì)自動(dòng)給項(xiàng)目構(gòu)建 debugrelease 版本。兩個(gè)版本的區(qū)別在于能否在安全設(shè)備(非 dev)上調(diào)試,以及 APK 如何簽名。debug 使用通過通用的 name/password 對生成的密鑰證書進(jìn)行簽名(為了防止在構(gòu)建過程中出現(xiàn)認(rèn)證請求)。release 在構(gòu)建過程中不進(jìn)行簽名,需要自行簽名。

這些配置是通過 BuildType 對象來完成的。默認(rèn)情況下,debugrelease 實(shí)例都會(huì)被創(chuàng)建。Android plugin 允許像創(chuàng)建其他 Build Type 一樣自定義這兩種類型。在 buildTypes 的 DSL 容器中進(jìn)行配置:

android {
    buildTypes {
        debug {
            applicationIdSuffix ".debug"
        }

        jnidebug {
            initWith(buildTypes.debug)// 復(fù)制
            packageNameSuffix ".jnidebug"
            jnidebugBuild true
        }
    }
}

另外,每個(gè) Build Type 都會(huì)創(chuàng)建一個(gè)新的 assemble<BuildTypeName> 任務(wù)。
例如:assembleDebug。
當(dāng) debug 和 release 構(gòu)建類型被預(yù)創(chuàng)建的時(shí)候,assembleDebug 和 assembleRelease 會(huì)被自動(dòng)創(chuàng)建。

buildType 的屬性和方法

https://developer.android.com/reference/tools/gradle-api

屬性 描述
applicationIdSuffix 應(yīng)用 id 后綴
name build type的名字
versionNameSuffix 版本名稱后綴
minifyEnabled 是否混淆
proguardFiles 混淆文件
signingConfig 簽名配置
multiDexEnabled 是否拆成多個(gè)Dex
方法 描述
buildConfigField(type, name, value) 添加一個(gè)變量生成 BuildConfig 類
consumerProguardFile(proguardFile) 添加一個(gè)混淆文件進(jìn)arr包
consumeProguardFile(proguardFiles) 添加混淆文件進(jìn)arr包
externalNativeBuild(action) 配置本地的build選項(xiàng)
initWith(that) 復(fù)制這個(gè) build 類型的所有屬性
proguardFile(proguardFile) 添加一個(gè)新的混淆配置文件
proguradFiles(files) 添加新的混淆文件
resValue(type, name, value) 添加一個(gè)新的生成資源
setProguardFiles(proguardFileIterable) 設(shè)置一個(gè)混淆配置文件

啟用 proguard 混淆

可以為不同的 buildTypes 選擇是否啟用混淆,一般 release 發(fā)布版本是需要啟用混淆的,這樣別人反編譯之后就很難分析你的代碼,而我們自己開發(fā)調(diào)試的時(shí)候是不需要混淆的,所以 debug 不啟用混淆。對 release 啟用混淆的配置如下:

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFile 'proguard.cfg'
        }
   }
}
  • minifyEnabled 為 true 表示啟用混淆
  • proguardFile 是混淆使用的配置文件
    這里是 module 根目錄下的 proguard.cfg 文件

啟用 zipAlign

這個(gè)也是比較簡單的,同樣也是在 buildTypes 里配置,可以為不用的 buildTypes 選擇時(shí)候開啟zipAlign

android {
    buildTypes {
        release {
            zipAlignEnabled true
        }
   }
}

配置應(yīng)用的簽名信息 android.signingConfigs{}

android.signingConfigs{} 下定義一個(gè)或者多個(gè)簽名信息,在 buildTypes{} 配置使用即可。比如:

android {
    signingConfigs {
        release {
            storeFile file("release.keystore")
            keyAlias "tina"
            keyPassword "123456"
            storePassword "123456"
        }
        debug {
            ...
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
        debug {
            signingConfig signingConfigs.debug
        }
    }
  }
  • storeFile:簽名證書文件
  • keyAlias:別名
  • keyPassword:key的密碼
  • storePassword:證書的密碼

配好相關(guān)信息即可在 buildTypes 配置使用

多渠道打包 android.productFlavors{}

Android Gradle 給我們提供了productFlavors,讓我們可以對生成的 APK 包進(jìn)行定制,所以就有了多渠道。它支持與 defaultConfig 相同的屬性,這是因?yàn)椋?code>defaultConfig 實(shí)際上屬于ProductFlavor

android  {
    productFlavors {
        dev{

        }
        google{

        }
        baidu{

        }
    }
}

這樣當(dāng)我們運(yùn)行 assembleRelease 的時(shí)候就會(huì)生成 3 個(gè) release 包,分別是 dev、google 以及 baidu。目前看這三個(gè)包除了文件名沒有什么不一樣,此時(shí)還沒有定制,使用的都是 defaultConfig 配置。這里的 flavor 和 defaultConfig 是一樣的,可以自定義其 applicationId、versionCode 以及 versionName等信息,比如區(qū)分不同包名:

android  {
    defaultConfig{
        ……
        // Specifies a flavor dimension.
        flavorDimensions "channel"
    }
    productFlavors {
        dev{
            applicationId "cn.tina.demo.dev"
        }
        google{
            applicationId "cn.tina.demo.google"
        }
    }
}

flavorDimensions(風(fēng)格維度),所有變種都必須屬于一個(gè)指定的變種維度,即一個(gè)產(chǎn)品變種組。必須將所有變種分配給某個(gè)變種維度;否則,您將收到如下所示的構(gòu)建錯(cuò)誤。如果給定的模塊僅指定一個(gè)變種維度,則 Android Gradle 插件會(huì)自動(dòng)將該模塊的所有變種分配給該維度。

 Error:All flavors must now belong to a named flavor dimension.
 The flavor 'flavor_name' is not assigned to a flavor dimension.

注意:flavor 的命名不能與已存在的 Build Type 或者與 androidTest、test sourceSet 有沖突。

每一個(gè) Build Type 都會(huì)生成新的 APK。Product Flavors 同樣也會(huì)做這些事情:項(xiàng)目的輸出將會(huì)組合所有的 Build Types 和 Product Flavors(如果有定義 Flavor)。

每一種組合(包含 Build Type 和 Product Flavor)就是一個(gè) Build Variant(構(gòu)建變種版本)。

例如,在之前的 Flavor 聲明例子中與默認(rèn)的 debug 和 release 兩個(gè) Build Types 將會(huì)生成 4 個(gè) Build Variants:{dev, google}{debug, release}

  • dev - debug
  • dev - release
  • google - debug
  • google - release

項(xiàng)目中如果沒有定義 flavor 同樣也會(huì)有 Build Variants,只是使用的是 default 的 flavor/config,因?yàn)槭菬o名稱的,所以生成的 build variant 列表看起來就跟 Build Type 列表一樣。

多定制的變種版本

擴(kuò)展:flavorDimesions 可以設(shè)置多個(gè),如

android  {
   defaultConfig{
       ……
       // Specifies a flavor dimension.
       flavorDimensions "channel","production"
   }
   productFlavors {
       dev{
           dimension "channel"
           applicationId "cn.tina.demo.dev"
       }
       google{
           dimension "channel"
           applicationId "cn.tina.demo.google"
       }
       productA{
         dimension "production"  
       }
       productB{
         dimension "production"  
       }
   }
}

那么可以組合出4種(2*2)種產(chǎn)品,分別有 debug 和 release,一共是8個(gè)變種版本

構(gòu)建和任務(wù)

我們前面提到每一個(gè) Build Type 會(huì)創(chuàng)建自己的 assemble<name> task,但是 Build Variants 是 Build Type 和 Product Flavor 的組合。
當(dāng)使用 Product Flavor 的時(shí)候,將會(huì)創(chuàng)建更多的 assemble-type task。分別是:

  • assemble<Variant Name> 允許直接構(gòu)建一個(gè) variant 版本,例如 assembleDevDebug

  • assemble<Build Type Name> 允許構(gòu)建指定 Build Type 的所有 APK,例如 assembleDebug 將會(huì)構(gòu)建 DevDebugGoogleDebug 兩個(gè) variant 版本。

  • assemble<Product Flavor Name> 允許構(gòu)建指定 flavor 的所有 APK,例如 assembleDev 將會(huì)構(gòu)建 DevDebugDevRelease 兩個(gè) variant 版本。
    另外 assemble task 會(huì)構(gòu)建所有的 variant 版本。

android.defaultConfig

AndroidManifest 里的占位符

AndroidManifest.xml 這是一個(gè)很重要的文件,我們的很多配置都在這里定義。有時(shí)候我們的一些配置信息,比如一個(gè)第三方應(yīng)用的 key,第三方統(tǒng)計(jì)分析的渠道號(hào)等也要在這里進(jìn)行配置。

這里以友盟統(tǒng)計(jì)分析平臺(tái)為例,演示這一功能的使用。在友盟統(tǒng)計(jì)分析中,我們需要根據(jù)渠道進(jìn)行統(tǒng)計(jì),比如 google,百度,應(yīng)用寶等渠道的活躍新增等。

友盟的 SDK 是在 AndroidManifest 里配置一個(gè) name 為 UMENG_CHANNEL 的 meta-data,這樣這個(gè) meta-data 的值就表示這個(gè) apk 是哪個(gè)渠道,我們版本發(fā)布有幾十個(gè)渠道,manifestPlaceholders 允許動(dòng)態(tài)替換在 AndroidManifest 文件里定義的占位符。

<meta-data 
    android:value="${UMENG_CHANNEL_VALUE}"
    android:name="UMENG_CHANNEL"
/>

如上 ${UMENG_CHANNEL_VALUE} 就是一個(gè)占位符,然后我們在 gradle 的 defaultConfig 里這樣定義腳本:

android {
    defaultConfig {
        manifestPlaceholders = [UMENG_CHANNEL_VALUE: 'dev']
    }
}

上面代碼塊的意思就是我們的默認(rèn)配置里 AndroidManifest 的 ${UMENG_CHANNEL_VALUE} 占位符會(huì)被 dev 這個(gè)字符串所替換,也就說默認(rèn)運(yùn)行的版本是一個(gè)開發(fā)版。以此類推,我們其他渠道的版本就可以這樣定義:

android  {
    productFlavors {
        google{
            applicationId "org.demo.google"
            manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'google')
        }
        baidu{
            applicationId "org.demo.baidu"
            manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'baidu')
        }
    }
}

這樣有多少個(gè)渠道就做多少次這樣的定義,即可完成分渠道統(tǒng)計(jì)。但是如果上百個(gè)渠道,這樣一個(gè)個(gè)寫的確太累,很麻煩,我們繼續(xù)研究,同學(xué)們有沒有發(fā)現(xiàn),我們的渠道名字和我們的 flavorName 一樣,我們用這個(gè) flavorName 作為 UMENG_CHANNEL_VALUE 不就好了嗎,可以批量的替換嗎?當(dāng)然可以,這又體現(xiàn)了我們 Gradle 的強(qiáng)大和靈活之處。

productFlavors.all { flavor ->
        manifestPlaceholders.put("UMENG_CHANNEL_VALUE",name)
}

循環(huán)每個(gè) flavor,并把他們的 UMENG_CHANNEL_VALUE 設(shè)置為他們自己的 name 名字。

BuildConfig 自定義常量

public final class BuildConfig {
  public static final boolean DEBUG = Boolean.parseBoolean("true");
  public static final String BUILD_TYPE = "debug";
}

BuildConfig.java 是 Android Gradle 自動(dòng)生成的一個(gè) java 類文件,無法手動(dòng)編譯,但是可以通過 Gradle 控制,也就是說這里是動(dòng)態(tài)可配置的,這里以生產(chǎn)環(huán)境和測試環(huán)境為例來說明該功能的使用。

我們在開發(fā) App 的時(shí)候免不了要和服務(wù)器進(jìn)行通信,我們的服務(wù)器一般都有生產(chǎn)和測試環(huán)境,當(dāng)我們處理開發(fā)和測試的時(shí)候使用測試環(huán)境進(jìn)行調(diào)試,正式發(fā)布的時(shí)候使用生成環(huán)境。以前的時(shí)候我們通過把不同的配置文件打包進(jìn) APK 中來控制,現(xiàn)在不一樣了,我們有更簡便的方法,這就是 buildConfigField。

android {
    defaultConfig {
        buildConfigField'String','API_SERVER_URL','"http://test.cn/"'
    }
    productFlavors {
        google{
            buildConfigField 'String','API_SERVER_URL','"http://release.cn/"'
        }
        baidu{
            buildConfigField 'String','API_SERVER_URL','"http://release.cn/"'
        }
    }
}

buildConfigField 一共有3個(gè)參數(shù),

  • 第一個(gè)是數(shù)據(jù)類型,是定義的常量值是一個(gè)什么類型,和 Java 的類型是對等的,這里是String。
  • 第二個(gè)參數(shù)是常量名,這里是 API_SERVER_URL。
  • 第三個(gè)參數(shù)是常量值。如此定義之后,就會(huì)在 BuildConfig.java 中生成一個(gè)常量名為 API_SERVER_URL 的常量定義。默認(rèn)配置的生成是:
public final static String API_SERVER_URL = "http://test.cn/"

當(dāng)是 baidu 和 google 渠道的時(shí)候生成的就是 http://release.cn/ 了。這個(gè)常量可以在我們編碼中引用。在我們進(jìn)行打包的時(shí)候會(huì)根據(jù) Gradle 配置動(dòng)態(tài)替換。

我們發(fā)現(xiàn)一般渠道版本都是用來發(fā)布的,肯定用的是生產(chǎn)服務(wù)器,所以我們可以使用批處理來搞定這個(gè)事情,而不用在一個(gè)個(gè)渠道里寫這些配置。

productFlavors.all { flavor ->
        buildConfigField 'String','API_SERVER_URL','"http://release.org/"'
    }

此外,比如 Gradle 的 resValue,也和 buildConfigField 一樣,只不過控制生成的是資源,比如我們在 android 的 values.xml 定義生成的字符串。可以用它來動(dòng)態(tài)生成我們想要的字符串,比如應(yīng)用的名字,可能一些渠道會(huì)不一樣,這樣就可以很靈活的控制自動(dòng)生成,關(guān)于 resValue 詳細(xì)介紹請參考相關(guān)文檔,這里不再舉例說明。

參考:https://developer.android.com/studio/build

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容