使用 Gradle 命令了解项目构建信息

引言

首先,Gradle 作为使用 Android Studio 开发 Android 项目的默认构建工具,它里面的任何东西都基于两个概念:

  • projects ( 项目 )
  • tasks ( 任务 )

每一个构建由一个或多个 projects 构成,每一个 project 由一个或多个 tasks 构成。每一个 task 代表细化的构建节点,比如 APK 的构建过程:

  1. 通过 aapt 打包 res 资源文件,生成 R.java、resources.arsc 和 res 文件(二进制和非二进制如 res/raw 和 pic 保持原样)
    2. 处理 .aidl 文件,生成对应的 Java 接口文件
    3. 通过 Java Compiler 编译 R.java、Java 接口文件、Java 源文件,生成 .class 文件
    4. 通过 dx/d8 工具,将 .class 文件和第三方库中的 .class 文件处理生成 classes.dex
    5. 通过 apkbuilder 工具,将 aapt 生成的 resources.arsc 和 res 文件、assets 文件和 classes.dex 一起打包生成 apk
    6. 通过 Jarsigner 工具,对上面的 apk 进行 debug 或 release 签名
    7. 通过 zipalign 工具,对签名后的 apk 进行对齐处理

在这个构建过程中的每一步,都有与之相关的 task 相呼应:

> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuild UP-TO-DATE
> Task :app:mergeDebugNativeDebugMetadata NO-SOURCE
> Task :app:compileDebugAidl NO-SOURCE  //处理 aidl
> Task :app:compileDebugRenderscript NO-SOURCE
> Task :app:generateDebugBuildConfig UP-TO-DATE
> Task :app:checkDebugAarMetadata UP-TO-DATE
> Task :app:generateDebugResValues UP-TO-DATE
> Task :app:generateDebugResources UP-TO-DATE
> Task :app:mergeDebugResources UP-TO-DATE  //合并资源文件
> Task :app:packageDebugResources UP-TO-DATE
> Task :app:mapDebugSourceSetPaths UP-TO-DATE
> Task :app:parseDebugLocalResources UP-TO-DATE
> Task :app:createDebugCompatibleScreenManifests UP-TO-DATE
> Task :app:extractDeepLinksDebug UP-TO-DATE
> Task :app:processDebugMainManifest UP-TO-DATE
> Task :app:processDebugManifest UP-TO-DATE
> Task :app:processDebugManifestForPackage UP-TO-DATE
> Task :app:javaPreCompileDebug UP-TO-DATE
> Task :app:mergeDebugShaders
> Task :app:compileDebugShaders NO-SOURCE
> Task :app:generateDebugAssets UP-TO-DATE
> Task :app:mergeDebugAssets  //合并 assets 文件
> Task :app:compressDebugAssets
> Task :app:processDebugJavaRes NO-SOURCE
> Task :app:checkDebugDuplicateClasses
> Task :app:mergeDebugJniLibFolders
> Task :app:mergeLibDexDebug
> Task :app:mergeDebugNativeLibs NO-SOURCE
> Task :app:stripDebugDebugSymbols NO-SOURCE
> Task :app:validateSigningDebug
> Task :app:writeDebugAppMetadata
> Task :app:writeDebugSigningConfigVersions
> Task :app:desugarDebugFileDependencies
> Task :app:processDebugResources  // aapt 打包资源
> Task :app:mergeExtDexDebug
> Task :app:compileDebugKotlin UP-TO-DATE  // 编译 kotlin 文件
> Task :app:compileDebugJavaWithJavac UP-TO-DATE  //编译 java 文件
> Task :app:dexBuilderDebug  //.class 文件转换为 dex 归档文件
> Task :app:mergeProjectDexDebug
> Task :app:mergeDebugJavaResource
> Task :app:packageDebug  //打包 apk
> Task :app:createDebugApkListingFileRedirect
> Task :app:assembleDebug

Task(任务)作为构建过程中的最小原子工作单元。所以要了解 Android 项目的相关构建信息,其实就是了解相关 Gradle Tasks。

在日常开发中,如果接触一个新项目,可能最先想了解的是:

  • 这个项目有哪些个 Project?
  • 这些Project 的依赖关系是怎么样的?
  • 你要参与的Project 使用了哪些技术库?

如果一直在开发某项目,可能会时不时用到:

  • 输出项目库依赖信息,找到某个库的依赖情况?
  • 升级某个库,需要升级哪些依赖它的库,以及影响哪些业务模块?
  • 输出项目依赖的 so 情况,找到 so 来源?

下面将简单介绍在 Android 项目构建中,会用到的一些 Gradle Task 命令。

备注:假设你已经对 Task 有所提前了解。

Gradle 命令

首先,在命令行执行的每一个 gradle 命令,其实都是执行的一个 task:

 ./gradlew 

> Task :help

Welcome to Gradle 7.3.3.

To run a build, run gradlew <task> ...

To see a list of available tasks, run gradlew tasks

To see more detail about a task, run gradlew help --task <task>

To see a list of command-line options, run gradlew --help

For more detail on using Gradle, see https://docs.gradle.org/7.3.3/userguide/command_line_interface.html

For troubleshooting, visit https://help.gradle.org

BUILD SUCCESSFUL in 2s
1 actionable task: 1 executed

可以看到输入./gradlew 命令,其实是执行的 help task: ./gradlew :help

每一个 task 的基础信息包含:Path(路径), Type(类型),Options(选项),Description(描述),Group(分组):

./gradlew help --task :help            

> Task :help
Detailed task information for :help

Path
     :help

Type
     Help (org.gradle.configuration.Help)

Options
     --task     The task to show help for.

Description
     Displays a help message.

Group
     help

Type(类型)可以在自定义Gradle Plugin 时,通过依赖:com.android.tools.build:gradle,查看 Task 源码。

task 信息有助于了解该 task 在项目构建中的作用和功能。

知道上面基本情况后,下面就开始了解项目构建信息。

基础信息

tasks

Task :tasks 用于输出项目所有的 task:

./gradlew :tasks

> Task :tasks

------------------------------------------------------------
Tasks runnable from root project 'gradle_command_demo'
------------------------------------------------------------

Android tasks
-------------
androidDependencies - Displays the Android dependencies of the project.
signingReport - Displays the signing info for the base and test modules
sourceSets - Prints out all the source sets defined in this project.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'gradle_command_demo.
dependencies - Displays all dependencies declared in root project gradle_command_demo'.
dependencyInsight - Displays the insight into a specific dependency in root project 'gradle_command_demo'.
help - Displays a help message.
javaToolchains - Displays the detected java toolchains.
outgoingVariants - Displays the outgoing variants of root project 'gradle_command_demo'.
projects - Displays the sub-projects of root project 'gradle_command_demo'.
properties - Displays the properties of root project 'gradle_command_demo'.
tasks - Displays the tasks runnable from root project 'gradle_command_demo' (some of the displayed tasks may belong to subprojects).

//......省略

上面输出的是根项目相关的所有task,包含自定义的 task。如果要输出子项目的所有task,可以是使用:/gradlew :<module>:tasks,也可以使用:./gradlew :tasks --all。如果要查看单个 task 的信息,可以使用:./gradlew :help --task <task> ,其中<task>是 task 的 Path。

buildEnvironment

Task :buildEnvironment 用于输出根项目的所有构建依赖,也就是根项目下 build.gradle 中声明的 dependencies 依赖详细信息:

./gradlew :buildEnvironment

> Task :buildEnvironment

------------------------------------------------------------
Root project 'gradle_command_demo'
------------------------------------------------------------

classpath
+--- com.android.tools.build:gradle:7.2.0
|    +--- com.android.tools:sdk-common:30.2.0
//......省略
+--- org.greenrobot:greendao-gradle-plugin:3.3.0
|    \--- org.greenrobot:greendao-code-modifier:3.3.0
//......省略
+--- com.google.protobuf:protobuf-gradle-plugin:0.8.18
|    +--- com.google.guava:guava:27.0.1-jre -> 30.1.1-jre (*)
//......省略
\--- org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21
     +--- org.jetbrains.kotlin:kotlin-gradle-plugin-api:1.6.21
//......省略

可以看到根项目依赖的 gradle 和 kotlin 插件相关信息,以及自定义或三方插件信息。

projects

Task :projects 用于输出项目列表:

./gradlew :projects

> Task :projects

------------------------------------------------------------
Root project 'gradle_command_demo'
------------------------------------------------------------

Root project 'gradle_command_demo'
+--- Project ':app'
+--- Project ':bgm'
+--- Project ':common'
|    +--- Project ':common:aiservice'
|    +--- Project ':common:rpc'
|    |    +--- Project ':common:rpc:proto-core'
|    |    \--- Project ':common:rpc:protogen'
+--- Project ':material'

可以看到该项目下所有的子项目,以及子项目层级。

propreties

Task :propreties 用于输出项目定义的属性信息,包含项目下gradle.properties 文件中定义的属性信息:

./gradlew :properties

> Task :properties

------------------------------------------------------------
Root project 'gradle_command_demo'
------------------------------------------------------------
allprojects: [root project 'gradle_command_demo', project ':app', project ':material', project ':bgm']
android.databinding.incremental: true
android.enableJetifier: true
android.injected.testOnly: false
android.lifecycleProcessor.incremental: true
android.useAndroidX: true

在实际项目中,属性信息包含项目基础信息、Java版本号、Plugin版本号、三方库版本号、Maven私有地址等。如果要查看某个子项目的属性信息,可以是使用:/gradlew :<module>:propreties

项目依赖

dependencies

Task :app:dependencies用于输出项目依赖项的树状结构,但在实际应用中,直接使用:./gradlew :<module>:dependencies会输出很多冗余信息。dependencies 有一个选项:

Options
     --configuration     The configuration to generate the report for.

configuration 有debug, release, debugAndroidTest, debugUnitTest, releaseUnitTest等阶段配置,这里选择 release 阶段 的 releaseRuntimeClasspath:Dependencies for runtime/packaging,那么就可以看到项目在打 release 包时的正式依赖项信息:

./gradlew :app:dependencies --configuration releaseRuntimeClassPath

> Task :app:dependencies

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

releaseRuntimeClasspath - Runtime classpath of compilation 'release' (target  (androidJvm)).
+--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21
|    +--- org.jetbrains.kotlin:kotlin-stdlib:1.6.21
|    |    +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.6.21
|    |    \--- org.jetbrains:annotations:13.0
|    \--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.6.21
|         \--- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 (*)
+--- androidx.core:core-ktx:1.7.0
+--- com.squareup.okhttp3:okhttp:3.12.13.18 (*)
//.....省略

如果 dependencies 信息太多,可以保存到文件:./gradlew :app:dependencies --configuration releaseRuntimeClassPath > dependencies.txt,这样更方便搜索和查看。项目依赖项信息非常有用,可以用来排查依赖冲突、分析库依赖关系等。

androidDependencies

上面的Task :app:dependencies 输出的是Android和Java 项目的依赖项,也就是有在项目build.gradle中声明:plugins { id 'com.android.application'}'plugins { id com.android.library'}plugins { id 'java-library'}的项目。

Task :app:androidDependencies用于输出项目依赖的Android 库信息,以平铺的方式输出,并包含所有的 configuration:

releaseRuntimeClasspath - Dependencies for runtime/packaging
+--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21@jar
+--- androidx.core:core-ktx:1.7.0@aar
+--- com.google.android.material:material:1.5.0@aar
+--- androidx.constraintlayout:constraintlayout:2.0.1@aar
//......省略

这个输出信息中除了关注依赖的 Android 库外,还可以关注所支持的 configuration 信息,configuration 有助于在自定义 task 中进行使用,也有助于下面的 dependencyInsight使用。

dependencyInsight

执行Task :app:dependencies后输出的信息,可以用于查看某个项目的依赖项树状结构,也可以用于查看某个依赖库在某个项目中的依赖情况,比如:project: app 依赖 com.squareup.okhttp3:okhttp:。但是要罗列出com.squareup.okhttp3:okhttp:被哪些项目和库依赖的所有信息,就不是很方便,这时就需要使用dependencyInsight

Task :app:dependencyInsight 用于输出项目中特定依赖项的详细信息,关于这个 task 的基础信息为:

Detailed task information for :app:dependencyInsight

Path
     :app:dependencyInsight

Type
     DependencyInsightReportTask (org.gradle.api.tasks.diagnostics.DependencyInsightReportTask)

Options
     --configuration     Looks for the dependency in given configuration.

     --dependency     Shows the details of given dependency.

     --singlepath     Show at most one path to each dependency

Description
     Displays the insight into a specific dependency in project ':app'.

Group
     help

使用这个 task 的方式是:./gradlew :app:dependencyInsight --configuration someConf --dependency someDep,必须有 --configuration 和 --dependency 选项。对于 --configuration 选项参数,可以通过上面的Task :app:androidDependencies 查看。

现在假设使用Task :app:dependencies 输出的信息如下:

> Task :app:dependencies

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

releaseRuntimeClasspath - Runtime classpath of compilation 'release' (target  (androidJvm)).
+--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.21
|    //......省略
+--- androidx.core:core-ktx:1.7.0
|    +--- org.jetbrains.kotlin:kotlin-stdlib:1.5.31 -> 1.6.21 (*)
|    +--- androidx.annotation:annotation:1.1.0 -> 1.3.0
|    \--- androidx.core:core:1.7.0
|         +--- androidx.annotation:annotation:1.2.0 -> 1.3.0
|    //......省略
+--- androidx.appcompat:appcompat:1.4.1
|    +--- androidx.annotation:annotation:1.3.0
|    +--- androidx.core:core:1.7.0 (*)
|    +--- androidx.cursoradapter:cursoradapter:1.0.0
|    |    \--- androidx.annotation:annotation:1.0.0 -> 1.3.0
|    +--- androidx.activity:activity:1.2.4
|    |    +--- androidx.annotation:annotation:1.1.0 -> 1.3.0
|    |    +--- androidx.core:core:1.1.0 -> 1.7.0 (*)
|    |    //......省略
|    +--- androidx.fragment:fragment:1.3.6
|    |    +--- androidx.annotation:annotation:1.1.0 -> 1.3.0
|    |    +--- androidx.core:core:1.2.0 -> 1.7.0 (*)
|    |    +--- androidx.collection:collection:1.1.0 (*)
|    |    +--- androidx.viewpager:viewpager:1.0.0
|    |    |    +--- androidx.annotation:annotation:1.0.0 -> 1.3.0
|    |    |    +--- androidx.core:core:1.0.0 -> 1.7.0 (*)
|    |    |    \--- androidx.customview:customview:1.0.0 -> 1.1.0
|    |    |         +--- androidx.annotation:annotation:1.1.0 -> 1.3.0
|    |    |         +--- androidx.core:core:1.3.0 -> 1.7.0 (*)
|    |    |         \--- androidx.collection:collection:1.1.0 (*)
//......省略
\--- com.google.android.material:material:1.5.0
     +--- androidx.annotation:annotation:1.2.0 -> 1.3.0
     +--- androidx.appcompat:appcompat:1.1.0 -> 1.4.1 (*)
     +--- androidx.cardview:cardview:1.0.0
     |    \--- androidx.annotation:annotation:1.0.0 -> 1.3.0
     +--- androidx.coordinatorlayout:coordinatorlayout:1.1.0
     |    +--- androidx.annotation:annotation:1.1.0 -> 1.3.0
     |    +--- androidx.core:core:1.1.0 -> 1.7.0 (*)
     |    +--- androidx.customview:customview:1.0.0 -> 1.1.0 (*)
     |    \--- androidx.collection:collection:1.0.0 -> 1.1.0 (*)
     +--- androidx.constraintlayout:constraintlayout:2.0.1
     |    +--- androidx.appcompat:appcompat:1.2.0 -> 1.4.1 (*)
     |    +--- androidx.core:core:1.3.1 -> 1.7.0 (*)
     |    \--- androidx.constraintlayout:constraintlayout-solver:2.0.1
     //......省略

(*) - dependencies omitted (listed previously)

那么要查看项目对androidx.core:core库的依赖情况,只能一条一条梳理:

  1. androidx.core:core-ktx:1.7.0 依赖 androidx.core:core:1.7.0
  2. androidx.appcompat:appcompat:1.4.1 依赖 androidx.core:core:1.7.0
  3. androidx.activity:activity:1.2.4 依赖 androidx.core:core:1.7.0
  4. androidx.fragment:fragment:1.3.6 依赖 androidx.core:core:1.2.0 -> 1.7.0 (*)
  5. 等等

(虽然不同库对 androidx.core:core库依赖版本不一样,但最终都只会使用一版本,使用哪一个版本由项目配置决定,可以使用1.2.0 ,也可以使用1.7.0。)

使用dependencyInsight罗列出androidx.core:core:1.7.0库被依赖的所有信息:

./gradlew :app:dependencyInsight --configuration releaseRuntimeClassPath --dependency androidx.core:core:1.7.0


> Task :app:dependencyInsight
androidx.core:core:1.7.0
   variant "releaseVariantReleaseRuntimePublication" [
      org.gradle.category                             = library (not requested)
      org.gradle.dependency.bundling                  = external (not requested)
      org.gradle.libraryelements                      = aar (not requested)
      org.gradle.usage                                = java-runtime
      org.gradle.status                               = release (not requested)

      Requested attributes not found in the selected variant:
         com.android.build.api.attributes.BuildTypeAttr  = release
         org.gradle.jvm.environment                      = android
         com.android.build.api.attributes.AgpVersionAttr = 7.2.0
         org.jetbrains.kotlin.platform.type              = androidJvm
   ]
   Selection reasons:
      - By conflict resolution : between versions 1.7.0, 1.5.0, 1.1.0, 1.2.0, 1.0.1, 1.3.0, 1.3.1 and 1.0.0

androidx.core:core:1.7.0
+--- androidx.appcompat:appcompat:1.4.1
|    +--- releaseRuntimeClasspath
|    +--- com.google.android.material:material:1.5.0 (requested androidx.appcompat:appcompat:1.1.0)
|    |    \--- releaseRuntimeClasspath
|    \--- androidx.constraintlayout:constraintlayout:2.0.1 (requested androidx.appcompat:appcompat:1.2.0)
|         \--- com.google.android.material:material:1.5.0 (*)
\--- androidx.core:core-ktx:1.7.0
     \--- releaseRuntimeClasspath

androidx.core:core:1.0.0 -> 1.7.0
+--- androidx.dynamicanimation:dynamicanimation:1.0.0
|    \--- com.google.android.material:material:1.5.0
|         \--- releaseRuntimeClasspath
+--- androidx.legacy:legacy-support-core-utils:1.0.0
|    \--- androidx.dynamicanimation:dynamicanimation:1.0.0 (*)
+--- androidx.loader:loader:1.0.0
|    +--- androidx.fragment:fragment:1.3.6
|    |    +--- com.google.android.material:material:1.5.0 (requested androidx.fragment:fragment:1.0.0) (*)
|    |    +--- androidx.appcompat:appcompat:1.4.1
|    |    |    +--- releaseRuntimeClasspath
|    |    |    +--- com.google.android.material:material:1.5.0 (requested androidx.appcompat:appcompat:1.1.0) (*)
|    |    |    \--- androidx.constraintlayout:constraintlayout:2.0.1 (requested androidx.appcompat:appcompat:1.2.0)
|    |    |         \--- com.google.android.material:material:1.5.0 (*)
|    |    \--- androidx.viewpager2:viewpager2:1.0.0 (requested androidx.fragment:fragment:1.1.0)
|    |         \--- com.google.android.material:material:1.5.0 (*)
|    \--- androidx.legacy:legacy-support-core-utils:1.0.0 (*)
\--- androidx.viewpager:viewpager:1.0.0
     \--- androidx.fragment:fragment:1.3.6 (*)

//......省略

androidx.core:core:1.3.1 -> 1.7.0
\--- androidx.constraintlayout:constraintlayout:2.0.1
     \--- com.google.android.material:material:1.5.0
          \--- releaseRuntimeClasspath

androidx.core:core:1.5.0 -> 1.7.0
\--- com.google.android.material:material:1.5.0
     \--- releaseRuntimeClasspath

(*) - dependencies omitted (listed previously)

首先,可以看到androidx.core:core库在项目中有多个版本:

  Selection reasons:
      - By conflict resolution : between versions 1.7.0, 1.5.0, 1.1.0, 1.2.0, 1.0.1, 1.3.0, 1.3.1 and 1.0.0

其次,查看库被依赖的情况,例如:

androidx.core:core:1.7.0
+--- androidx.appcompat:appcompat:1.4.1
|    +--- releaseRuntimeClasspath
|    +--- com.google.android.material:material:1.5.0 (requested androidx.appcompat:appcompat:1.1.0)
|    |    \--- releaseRuntimeClasspath
|    \--- androidx.constraintlayout:constraintlayout:2.0.1 (requested androidx.appcompat:appcompat:1.2.0)
|         \--- com.google.android.material:material:1.5.0 (*)
\--- androidx.core:core-ktx:1.7.0
     \--- releaseRuntimeClasspath

androidx.core:core:1.7.0 被 androidx.appcompat:appcompat:1.4.1 依赖,androidx.appcompat:appcompat:1.4.1 被 com.google.android.material:material:1.5.0 依赖,com.google.android.material:material:1.5.0 作为依赖的源头。以此类推,androidx.core:core:1.7.0 被 androidx.core:core-ktx:1.7.0 依赖,androidx.core:core-ktx:1.7.0 作为依赖的源头。

以此,可以看到androidx.core:core:1.7.0库在项目中被依赖的全部详细信息,这将有助于做库的版本升级。

在这里,如果只是为了查看androidx.core:core,可以不区分版本号:

./gradlew :app:dependencyInsight --configuration releaseRuntimeClassPath --dependency androidx.core:core

另外,如果要查看依赖项的路径,可以添加选项 --singlepath(每个依赖项最多显示一个路径):

./gradlew :app:dependencyInsight --configuration releaseRuntimeClassPath --dependency androidx.core:core:1.7.0 --singlepath

> Task :app:dependencyInsight
androidx.core:core:1.7.0
   variant "releaseVariantReleaseRuntimePublication" [
      org.gradle.category                             = library (not requested)
      org.gradle.dependency.bundling                  = external (not requested)
      org.gradle.libraryelements                      = aar (not requested)
      org.gradle.usage                                = java-runtime
      org.gradle.status                               = release (not requested)

      Requested attributes not found in the selected variant:
         com.android.build.api.attributes.BuildTypeAttr  = release
         org.gradle.jvm.environment                      = android
         com.android.build.api.attributes.AgpVersionAttr = 7.2.0
         org.jetbrains.kotlin.platform.type              = androidJvm
   ]
   Selection reasons:
      - By conflict resolution : between versions 1.7.0, 1.5.0, 1.1.0, 1.2.0, 1.0.1, 1.3.0, 1.3.1 and 1.0.0

androidx.core:core:1.7.0
\--- androidx.appcompat:appcompat:1.4.1
     \--- releaseRuntimeClasspath

androidx.core:core:1.0.0 -> 1.7.0
\--- androidx.dynamicanimation:dynamicanimation:1.0.0
     \--- com.google.android.material:material:1.5.0
          \--- releaseRuntimeClasspath

androidx.core:core:1.0.1 -> 1.7.0
\--- androidx.appcompat:appcompat-resources:1.4.1
     \--- androidx.appcompat:appcompat:1.4.1
          \--- releaseRuntimeClasspath

androidx.core:core:1.1.0 -> 1.7.0
\--- androidx.activity:activity:1.2.4
     \--- androidx.appcompat:appcompat:1.4.1
          \--- releaseRuntimeClasspath

androidx.core:core:1.2.0 -> 1.7.0
\--- androidx.drawerlayout:drawerlayout:1.1.1
     \--- com.google.android.material:material:1.5.0
          \--- releaseRuntimeClasspath

androidx.core:core:1.3.0 -> 1.7.0
\--- androidx.customview:customview:1.1.0
     \--- androidx.drawerlayout:drawerlayout:1.1.1
          \--- com.google.android.material:material:1.5.0
               \--- releaseRuntimeClasspath

androidx.core:core:1.3.1 -> 1.7.0
\--- androidx.constraintlayout:constraintlayout:2.0.1
     \--- com.google.android.material:material:1.5.0
          \--- releaseRuntimeClasspath

androidx.core:core:1.5.0 -> 1.7.0
\--- com.google.android.material:material:1.5.0
     \--- releaseRuntimeClasspath

可以看到依赖的每一个路径。

构建选项

使用 ./gradlew --help 可以看到构建支持的所有选项操作:

USAGE: gradlew [option...] [task...]

-?, -h, --help                     Shows this help message.
-a, --no-rebuild                   Do not rebuild project dependencies.
-b, --build-file                   Specify the build file. [deprecated]
--build-cache                      Enables the Gradle build cache. Gradle will try to reuse outputs from previous builds.
//......省略

每个选项都有助于分析构建时的问题,下面将对其中几个选项进行简单介绍。

–profile

–profile 选项输出构建时会执行的任务列表,以及每一个任务耗时情况:

./gradlew :app:assembleDebug --profile

执行完后,会输出一个 html 格式的报告文档:
在这里插入图片描述
在这个报告文档中,有每一个任务的执行时长明细,这对优化构建时间非常有用。

备注:优化构建时间,对于开发来说,是首要的事情,因为开发每天花费时间最多的就是构建应用程序。

-q

-q 选项表示静默构建,只会打印出 error 信息:

./gradlew :app:assembleDebug -q

可以节约构建时间。

–offline

–offline 选项表示构建时不访问网络资源:

./gradlew :app:assembleDebug --offline -q 

默认情况,构建时会访问网络资源,比如访问 Maven 仓库资源。因此使用 --offline 选项也可以节约构建时间。在开发阶段,第一次构建完成后,后续在只更改代码或资源文件的情况下,构建都可以使用 --offline。

–info

–info 选项表示构建时,打印出 info 信息:

./gradlew :app:assembleDebug --info

info 信息包含构建时,所有 task 相关的信息,这有助于排查构建问题,或帮助熟悉项目构建情况。

–s

–s 选项表示构建时,打印出异常的所有栈信息:

./gradlew :app:assembleDebug --s

这有助于排查构建问题。

–refresh-dependencies

–refresh-dependencies 选项表示构建时,刷新相关依赖:

./gradlew :app:assembleDebug --refresh-dependencies

如果更新了项目相关依赖库版本,在构建后发现没有生效,就可以使用这个选项。

–no-build-cache

–no-build-cache 选项表示构建时,不使用构建缓存:

./gradlew :app:assembleDebug --no-build-cache

如果更改了项目代码,在构建后发现没有生效,就可以使用这个选项,或者执行 clean。

备注:这个选项在 Gitlab CI 中构建,或远程构建项目时,发现更改代码没有生效,可以选择使用。

so 依赖信息

Task :app:dependenciesTask :app:androidDependencies 可以输出项目依赖的 aar 信息,但要输出项目依赖的 so 信息,并没有相关的 task 或者构建选项可以直接使用。

在 apk 产物中,项目依赖的本地或三方 so 都会放在 lib 文件夹下面:
在这里插入图片描述
另外,构建 apk 时,打包 res 资源文件、处理 .aidl 文件、生成 .class 文件等都有相关的 task 相对应。那么,将 so 都放到 lib 文件下面,可以猜测也应该有相关的 task 相对应。

在构建项目时 ./gradlew :app:assembleDebug --console=plain

//......省略
> Task :app:mergeDebugJniLibFolders
> Task :app:mergeLibDexDebug
> Task :app:mergeDebugNativeLibs NO-SOURCE
> Task :app:stripDebugDebugSymbols NO-SOURCE
 //......省略

可以看到其中有一个 task:Task :app:mergeDebugNativeLibs NO-SOURCE,再通过 help 看下这个 task 的基础信息:

> Task :help
Detailed task information for :app:mergeDebugNativeLibs

Path
     :app:mergeDebugNativeLibs

Type
     MergeNativeLibsTask (com.android.build.gradle.internal.tasks.MergeNativeLibsTask)

Description
     -

Group
     -

没有描述,通过 Type 查看源码:

/**
 * Task to merge native libs from a project and possibly its dependencies
 */
@DisableCachingByDefault
abstract class MergeNativeLibsTask : NonIncrementalTask() {
   @get:InputFiles
    @get:PathSensitive(PathSensitivity.RELATIVE)
    @get:SkipWhenEmpty
    @get:IgnoreEmptyDirectories
    abstract val projectNativeLibs: ConfigurableFileCollection //本项目 so 文件列表

    @get:InputFiles
    @get:PathSensitive(PathSensitivity.RELATIVE)
    @get:SkipWhenEmpty
    @get:IgnoreEmptyDirectories
    abstract val subProjectNativeLibs: ConfigurableFileCollection //子项目 so 文件列表

    @get:InputFiles
    @get:PathSensitive(PathSensitivity.RELATIVE)
    @get:SkipWhenEmpty
    @get:IgnoreEmptyDirectories
    abstract val externalLibNativeLibs: ConfigurableFileCollection //三方库 so 文件列表
}

这个任务其实就是合并项目本地依赖或三方依赖的Native库。那么,要输出项目依赖的 so 信息,就可以通过 Task:app:mergeDebugNativeLibs 实现。

在 app 下的 build.gradle 中添加 Task:app:mergeDebugNativeLibs 相关监听:

project.afterEvaluate {
    project.android.applicationVariants.all { variant ->
        //获取构建类型名称 debug 或 release 或其它
        def buildTypeName = variant.buildType.name
        def name = String.valueOf(buildTypeName.charAt(0)).toUpperCase() + buildTypeName.substring(1)
        def mergeNativeLibsTask = project.tasks.getByName("merge${name}NativeLibs")
        mergeNativeLibsTask.doLast { task ->
            logger.lifecycle("project native libs:")
            //当前项目相关的 so 文件列表
            logger.lifecycle("${task.projectNativeLibs.getFiles()}")
            logger.lifecycle("------")
            //子项目相关的 so 文件列表
            logger.lifecycle("sub project native libs:")
            logger.lifecycle("${task.subProjectNativeLibs.getFiles()}")
            logger.lifecycle("------")
            //三方库相关的 so 文件列表
            logger.lifecycle("external project native libs:")
            logger.lifecycle("${task.externalLibNativeLibs.getFiles()}")
        }
    }
}

然后执行 ./gradlew :app:mergeDebugNativeLibs

> Task :app:mergeDebugNativeLibs
project native libs:
[]
------
sub project native libs:
[/Users/wangjiang/Public/software/workplace/demo/tuwen/build/intermediates/library_jni/debug/jni, /Users/wangjiang/Public/software/workplace/demo/common/box2d/build/intermediates/library_jni/debug/jni]
------
external project native libs:
[/Users/wangjiang/.gradle/caches/transforms-3/e1e62cbfcd84bfede405cbb308e1bac9/transformed/jetified-weibo-7.7.0-btool-r3/jni, /Users/wangjiang/.gradle/caches/transforms-3/15985ccd1ee3aaa552f8eb8a95712cc3/transformed/jetified-android-database-sqlcipher-3.5.9/jni]

上面输出的信息可以看见 so 文件列表,但是信息比较粗糙,可以再精细一下:

project.afterEvaluate {
    project.android.applicationVariants.all { variant ->
        //获取构建类型名称 debug 或 release 或其它
        def buildTypeName = variant.buildType.name
        def name = String.valueOf(buildTypeName.charAt(0)).toUpperCase() + buildTypeName.substring(1)
        def mergeNativeLibsTask = project.tasks.getByName("merge${name}NativeLibs")
        mergeNativeLibsTask.doLast { task ->
            //当前项目相关的 so 文件列表
            printProjectSoInfo("project native libs:", false, task.projectNativeLibs.getFiles())
            //子项目相关的 so 文件列表
            printProjectSoInfo("sub project native libs:", false, task.subProjectNativeLibs.getFiles())
            //三方库相关的 so 文件列表
            printProjectSoInfo("external project native libs:", true, task.externalLibNativeLibs.getFiles())
        }
    }
}

class NativeLibInfo {
    String libName //项目或三方库名称
    List<String> soRelativePathList = new ArrayList<>() //相对项目或三方库的 so 路径

    @Override
    String toString() {
        return "{" + libName + ":" + soRelativePathList + "}"
    }
}

def printProjectSoInfo(String name, boolean isExternal, Set<File> fileSet) {
    logger.lifecycle(name)
    def projectNames = new HashSet<String>()
    def nativeLibsInfoList = new ArrayList<NativeLibInfo>()
    def rootProjectPath = project.rootProject.projectDir.path
    def buildName = project.rootProject.buildDir.name
    fileSet.forEach { file ->
        def projectName
        if (!isExternal) {
            projectName = file.path.substring(rootProjectPath.length() + 1, file.path.indexOf(buildName) - 1)
        } else {
            def parentName = file.parentFile.name
            projectName = parentName.substring(parentName.indexOf('-') + 1)
        }
        projectNames.add(projectName)
        def childFiles = file.listFiles().toList()
        def nativeLibInfo = new NativeLibInfo()
        nativeLibInfo.libName = projectName
        while (childFiles.size() > 0) {
            def childFile = childFiles.remove(0)
            if (childFile.isDirectory()) {
                childFiles.addAll(childFile.listFiles())
            } else {
                nativeLibInfo.soRelativePathList.add(childFile.path.substring(file.path.length() + 1))
            }
        }
        nativeLibsInfoList.add(nativeLibInfo)
    }
    logger.lifecycle("${projectNames}")
    logger.lifecycle("${nativeLibsInfoList}")
    logger.lifecycle("------")
}

再执行 ./gradlew :app:mergeDebugNativeLibs

> Task :app:mergeDebugNativeLibs
project native libs:
[]
[]
------
sub project native libs:
[tuwen, common/box2d]
[{tuwen:[armeabi-v7a/libBBStudioAudioSpeed.so, armeabi-v7a/libTuwenAudio.so, arm64-v8a/libBBStudioAudioSpeed.so, arm64-v8a/libTuwenAudio.so]}, {common/box2d:[armeabi-v7a/libMBox2d.so, armeabi-v7a/libbox2d.so, arm64-v8a/libMBox2d.so, arm64-v8a/libbox2d.so]}]
------
external project native libs:
[weibo-7.7.0-btool-r3,android-database-sqlcipher-3.5.9]
[{weibo-7.7.0-btool-r3:[armeabi-v7a/libweibosdkcore.so, armeabi-v7a/libwind.so, arm64-v8a/libweibosdkcore.so, arm64-v8a/libwind.so, armeabi/libweibosdkcore.so, armeabi/libwind.so]}, {android-database-sqlcipher-3.5.9:[armeabi-v7a/libsqlcipher.so, x86/libsqlcipher.so, arm64-v8a/libsqlcipher.so, armeabi/libsqlcipher.so, x86_64/libsqlcipher.so]}]

这时就可以看到哪个 so 被哪个项目或三方库依赖。当然,也可以自定义其它输出方式,比如输出一个例出项目 so 依赖信息的 html 报告文档。

总结

不管是接触一个新项目,还是一直开发老项目,使用 Gradle 命令,可以对项目构建信息有一个快速的掌握。要分析项目 aar 或 jar 依赖信息,可以使用 Task :app:dependencies./gradlew :app:dependencyInsight --configuration someConf --dependency someDep ;要分析项目 so 依赖信息,可以监听 Task :app:mergeDebugNativeLibs。依赖信息对于解决依赖冲突,库升级问题非常有帮助。另外,一些常用的构建选项如: --profile, --info, --refresh-dependencies, --no-build-cache, --offline等,也有助于节约构建时间,或排查构建问题。总之,了解或掌握一些 Gradle 命令,对于开发来说,是很有帮助的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/151693.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SOLIDWORKS功能布局实用技巧之保存实体技术

在SOLIDWORKS软件中&#xff0c;有一些命令可以将一个或多个实体保存为独立的零件文件。然而&#xff0c;每个命令都具有不同的特性&#xff0c;有些命令的选项可以让您在保存多个零件时直接生成装配体文件。让我们来深入了解这些功能布局技巧&#xff0c;特别是实体保存技术。…

XXX系统测试报告测试用例模板

XXX系统测试报告 编制&#xff1a; 2023-5-16 审核&#xff1a; 日期&#xff1a; 批准&#xff1a; 日期&#xff1a; 版本 修订时间 修订人 修订类型 修订章节 修订内容 *修订类型分为 A …

如何解决3d max渲染效果图全白这类异常问题?

通过3d max渲染效果图时&#xff0c;经常会出现3Dmax渲染效果图全黑或是3Dmax渲染效果图全白这类异常问题。可能遇到这类问题较多的都是新手朋友。不知如何解决。 3dmax渲染出现异常的问题&#xff0c;该如何高效解决呢&#xff1f;今天小编这里整理几项知识点&#xff0c;大家…

力扣刷题篇之数与位1

系列文章目录 目录 系列文章目录 前言 一、进制转换 总结 前言 本系列是个人力扣刷题汇总&#xff0c;本文是数与位。刷题顺序按照[力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 - 力扣&#xff08;LeetCode&#xff09; 一、进制转换 67. 二进制求和 - 力扣&…

二十、泛型(8)

本章概要 潜在的类型机制 pyhton 中的潜在类型C 中的潜在类型Go 中的潜在类型java 中的直接潜在类型 潜在类型机制 在本章的开头介绍过这样的思想&#xff0c;即要编写能够尽可能广泛地应用的代码。为了实现这一点&#xff0c;我们需要各种途径来放松对我们的代码将要作用的…

Leetcode周赛371补题(3 / 3)

目录 1、找出强数对的最大异或值 - 暴力 2、高访问员工 - 哈希表 模拟 3、最大化数组末位元素的最少操作次数 - 思维 贪心 1、找出强数对的最大异或值 - 暴力 找出强数对的最大异或值 I class Solution {public int maximumStrongPairXor(int[] a) {int na.length,max0;…

VS Code如何使用服务器的Python开发环境

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

写作脑科学——屠龙的高效写作指南

ISBN: 978-7-115-59231-6 作者&#xff1a;杨滢&#xff08;屠龙的胭脂井&#xff09; 页数&#xff1a;201页 阅读时间&#xff1a;2023-09-09 推荐指数&#xff1a;★★★★★ 十分推荐这本书&#xff0c;写的非常简单易懂&#xff0c;里面有很多方法论和实用技巧&#xff0c…

X86 bios 中断大全

1、显示服务(Video Service——INT 10H) 00H —设置显示器模式 0CH —写图形象素 01H —设置光标形状 0DH —读图形象素 02H —设置光标位置 0EH —在Teletype模式下显示字符 03H —读取光标信息 0FH —读取显示器模式 04H —读取光笔位置 10H —颜色…

【LeetCode刷题-滑动窗口】--1658.将x减到0的最小操作数

1658.将x减到0的最小操作数 思路与算法&#xff1a; 根据题目描述&#xff0c;在每一次操作中&#xff0c;可以移除数组nums最左边和最右边的元素&#xff0c;因此&#xff0c;在所有的操作完成后&#xff0c;数组nums的一个前缀以及一个后缀被移除&#xff0c;并且它们的和恰…

CopyOnWriteArrayList 源码详解

目录 一. 前言 二. 源码详解 2.1. 类结构 2.2. 属性 2.3. 构造方法 2.4. add(E e) 2.5. add(int index, E element) 2.6. addIfAbsent() 方法 2.7. 获取元素() 方法 2.8. remove(int index) 2.9. size() 三. FAQ 3.1. 为什么CopyOnWriteArrayList没有size属性&…

架构师的成名之路

相信大家都对未来的职业发展有着憧憬和规划&#xff0c;要做架构师、要做技术总监、要做CTO。对于如何实现自己的职业规划也都信心满满&#xff0c;努力工作、好好学习、不断提升自己。 相信成为一名优秀的架构师是很多程序员的目标&#xff0c;架构师的工作包罗万象&#xff…

3.3 Windows驱动开发:内核MDL读写进程内存

MDL内存读写是一种通过创建MDL结构体来实现跨进程内存读写的方式。在Windows操作系统中&#xff0c;每个进程都有自己独立的虚拟地址空间&#xff0c;不同进程之间的内存空间是隔离的。因此&#xff0c;要在一个进程中读取或写入另一个进程的内存数据&#xff0c;需要先将目标进…

基于SSM的校园服务平台管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

C++初阶,详解类和对象(2)

详解类和对象&#xff08;2&#xff09; 一&#xff0c;前言二&#xff0c;构造函数2.1构造函数概念2.2构造函数特性 三&#xff0c;析构函数3.1析构函数概念3.2析构函数特性 一&#xff0c;前言 上一篇我们讲了类的大体框架&#xff0c;这篇内容我们要重点来说一说类的几个默…

【Vue】内置指令真的很常用!

内置指令 v-text v-text用于将一个变量的值渲染为元素的文本内容 注意v-text只能接受字符串类型的值,对于对象类型,会调用toString()方法 与插值表达式区别就是它会替换标签中的值&#xff0c;只显示它绑定的&#xff08;还是插值语法用的多~&#xff09; 语法 <元素 …

普源DS1052E固件升级【附所有升级固件及工具】

折腾了两天&#xff0c;总算是弄好了。 升级的目的是啥&#xff1f;DS1052E的带宽是50M&#xff0c;示波器的时基最小可以调到5ns。固件升级后示波器的时基最小可以调到2ns&#xff0c;理论上说明此时示波器的带宽是100M。 网上的方法能找到很多&#xff0c;我总结一下大概的流…

jeesite 按部门过滤数据权限(保姆级图文教程)

文章目录 前言一、数据库表添加机构字段二、修改实体3.修改服务层总结前言 在项目开发过程中,数据需要按照部门、公司进行权限过滤,本篇文章记录下如何修改按部门进行权限过滤的详细图文教程。 一、数据库表添加机构字段 要进行权限过滤的表中添加机构字段 二、修改实体 添…

Java 算法篇-链表的经典算法:根据值删除节点、删除倒数第 n 个节点

&#x1f525;博客主页&#xff1a; 小扳_-CSDN博客 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 链表的创建 2.0 链表的经典算法 - 根据值来删除节点 2.1 根据值来删除节点 - 遍历链表来实现 2.2 根据值来删除节点 - 递归实现 3.0 链表的经典算法 - 删除倒数第 n…

Godot4.1 GDExtension 配置VisualStudio方法梳理以及快捷配置工具

写在最前 本篇教程基于之前教程&#xff0c;并且默认为Windows10&#xff0c;64位&#xff0c;Godot版本4.1.3如果遇到任何问题&#xff0c;欢迎及时提出&#xff0c;如果配置成功了请点个赞&#xff0c;球球啦。 之前教程 https://blog.csdn.net/qq_31805591/article/detai…