场景
- 在构建
Android App
的时候,最常用的构建工具就是gradle
,这个工具使用一种.gradle
的文本进行任务描述. 最常见的就是根据build.gradle
的配置指定使用gradle
的版本。接着根据项目的配置下载所依赖的jar
包并编译.
dependencies {
classpath 'com.android.tools.build:gradle:3.6.2'
}
- 我对
gradle
还没系统的学习过. 今天在编译google
的例子项目时在项目根目录使用命令行运行gradlew.bat
就报以下错误. 项目都是用的java
代码编写,何来的kotlin
错误?
* What went wrong:
A problem occurred configuring project ':app'.
> kotlin.KotlinNullPointerException (no error message)
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
说明
- 注意这个
gradle
是针对android
的构建插件,而android
目前官方推荐使用kotlin
语言开发, 所以gradle
插件需要集成kotlin
包也不奇怪. 我们根据提示运行增加参数gradlew.bat --stacktrace
. 在命令行运行输出了一段比较长的错误, 其中就有KotlinNullPointerException
抛出的源码位置. 可见在文件DataBindingCompilerArguments.kt
的 170 行的createArguments
方法抛出了异常. 而这个 170 行就是sdkDir = globalScope.sdkComponents.getSdkFolder()!!,
获取SDK
目录时抛出的异常。很明显了,就是获取不到Android SDK
目录.
Caused by: kotlin.KotlinNullPointerException
at com.android.build.gradle.internal.tasks.databinding.DataBindingCompilerArguments$Companio
n.createArguments(DataBindingCompilerArguments.kt:170)
-
那么设置
Android SDK
我所知道的有3种方法.- 在我的电脑右键属性->高级->环境变量->用户变量增加
ANDROID_HOME
作为变量名,E:\software\Android\sdk
你自己的Android SDK
安装目录. 这个变量值在所有新建的命令行有效. - 在当前命令行输入内容并回车
set ANDROID_HOME=E:\software\Android\sdk
,那么这个变量值在当前的命令行有效. - 在当前的项目目录增加一个
local.properties
的文本文件,增加文本值sdk.dir=E\:\\software\\Android\\sdk
即可.
- 在我的电脑右键属性->高级->环境变量->用户变量增加
-
设置完之后这个编译步骤就会成功,当然剩下的下载
jar
包可能由于网络不通造成下载失败就是另外的问题了。 -
至于这个文件
DataBindingCompilerArguments.kt
源码只能通过google
搜索,bing
是搜不到的. 至于这个类所在的包com.android.build.gradle.internal.tasks.databinding
和它所在的jar
在gradle
下载的包gradle-3.6.2.jar
里, 当然根据依赖的gradle
插件版本判断,我这里是com.android.tools.build:gradle:3.6.2
. 所以在3.6.2
目录下.
C:\Users\用户名\.gradle\caches\modules-2\files-2.1\com.android.tools.build\gradle\3.6.2\a5e817cf4833326b12f3e599f97486a04fe62756\gradle-3.6.2.jar
顺便说一下,gradle
下载的第三方 jar
,包括 gradle
的 android
插件包是在以下目录里:
C:\Users\用户名\.gradle\caches\modules-2\files-2.1
而安装的 gradle
,注意不是插件包, 是在以下目录:
C:\Users\用户名\.gradle\wrapper\dists
- 我们来看看这个项目的
.gitigore
忽略设置, 就包含了当前项目的local.properties
文件,所以这个项目里这个文件的是提交不了的.
*.iml
.idea
.gradle
/local.properties
.DS_Store
build/
/captures
.externalNativeBuild
源码参考
DataBindingCompilerArguments.kt
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.build.gradle.internal.tasks.databinding
import android.databinding.tool.CompilerArguments
import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.internal.scope.InternalArtifactType.DATA_BINDING_BASE_CLASS_LOG_ARTIFACT
import com.android.build.gradle.internal.scope.InternalArtifactType.DATA_BINDING_DEPENDENCY_ARTIFACTS
import com.android.build.gradle.internal.scope.InternalArtifactType.DATA_BINDING_LAYOUT_INFO_TYPE_MERGE
import com.android.build.gradle.internal.scope.InternalArtifactType.DATA_BINDING_LAYOUT_INFO_TYPE_PACKAGE
import com.android.build.gradle.internal.scope.InternalArtifactType.FEATURE_DATA_BINDING_BASE_FEATURE_INFO
import com.android.build.gradle.internal.scope.InternalArtifactType.FEATURE_DATA_BINDING_FEATURE_INFO
import com.android.build.gradle.internal.scope.VariantScope
import com.android.build.gradle.options.BooleanOption
import org.gradle.api.file.Directory
import org.gradle.api.file.RegularFile
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.Internal
import org.gradle.api.tasks.Optional
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.PathSensitive
import org.gradle.api.tasks.PathSensitivity
import org.gradle.process.CommandLineArgumentProvider
import java.io.File
/**
* Arguments passed to data binding. This class mimics the [CompilerArguments] class except that it
* also implements [CommandLineArgumentProvider] for input/output annotations.
*/
@Suppress("MemberVisibilityCanBePrivate")
class DataBindingCompilerArguments constructor(
@get:Input
val incremental: Boolean,
@get:Input
val artifactType: CompilerArguments.Type,
// Use module package provider so that we can delay resolving the module package until execution
// time (for performance). The resolved module package is set as @Input (see getModulePackage()
// below), but the provider itself should be set as @Internal.
private val modulePackageProvider: () -> String,
@get:Input
val minApi: Int,
// We can't set the sdkDir as an @InputDirectory because it is too large to compute a hash. We
// can't set it as an @Input either because it would break cache relocatability. Therefore, we
// annotate it with @Internal, expecting that the directory's contents should be stable and this
// won't affect correctness.
@get:Internal
val sdkDir: File,
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
val dependencyArtifactsDir: Provider<Directory>,
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
val layoutInfoDir: Provider<Directory>,
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
val classLogDir: Provider<Directory>,
@get:Optional
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
val baseFeatureInfoDir: Provider<Directory>,
@get:Optional
@get:InputFiles
@get:PathSensitive(PathSensitivity.RELATIVE)
val featureInfoDir: Provider<Directory>,
@get:Optional
@get:OutputDirectory
val aarOutDir: Provider<Directory>,
@get:Optional
@get:OutputFile
val exportClassListOutFile: File?,
@get:Input
val enableDebugLogs: Boolean,
// We don't set this as an @Input because: (1) it doesn't affect the results of data binding
// processing, and (2) its value is changed between an Android Studio build and a command line
// build; by not setting it as @Input, we allow the users to get incremental/UP-TO-DATE builds
// when switching between the two modes (see https://issuetracker.google.com/80555723).
@get:Internal
val printEncodedErrorLogs: Boolean,
@get:Input
val isTestVariant: Boolean,
@get:Input
val isEnabledForTests: Boolean,
@get:Input
val isEnableV2: Boolean
) : CommandLineArgumentProvider {
@Input
fun getModulePackage() = modulePackageProvider()
override fun asArguments(): Iterable<String> {
val arguments = CompilerArguments(
incremental = incremental,
artifactType = artifactType,
modulePackage = getModulePackage(),
minApi = minApi,
sdkDir = sdkDir,
dependencyArtifactsDir = dependencyArtifactsDir.get().asFile,
layoutInfoDir = layoutInfoDir.get().asFile,
classLogDir = classLogDir.get().asFile,
baseFeatureInfoDir = baseFeatureInfoDir.orNull?.asFile,
featureInfoDir = featureInfoDir.orNull?.asFile,
aarOutDir = aarOutDir.orNull?.asFile,
exportClassListOutFile = exportClassListOutFile,
enableDebugLogs = enableDebugLogs,
printEncodedErrorLogs = printEncodedErrorLogs,
isTestVariant = isTestVariant,
isEnabledForTests = isEnabledForTests,
isEnableV2 = isEnableV2
).toMap()
// Don't need to sort the returned list as the order shouldn't matter to Gradle.
// Also don't need to escape the key and value strings as they will be passed as-is to
// the Java compiler.
return arguments.map { entry -> "-A${entry.key}=${entry.value}" }
}
companion object {
@JvmStatic
fun createArguments(
variantScope: VariantScope,
enableDebugLogs: Boolean,
printEncodedErrorLogs: Boolean
): DataBindingCompilerArguments {
val globalScope = variantScope.globalScope
val variantData = variantScope.variantData
val variantConfig = variantScope.variantConfiguration
val artifacts = variantScope.artifacts
return DataBindingCompilerArguments(
incremental = globalScope.projectOptions
.get(BooleanOption.ENABLE_INCREMENTAL_DATA_BINDING),
artifactType = getModuleType(variantScope),
modulePackageProvider = { variantConfig.originalApplicationId },
minApi = variantConfig.minSdkVersion.apiLevel,
sdkDir = globalScope.sdkComponents.getSdkFolder()!!,
dependencyArtifactsDir =
artifacts.getFinalProduct(DATA_BINDING_DEPENDENCY_ARTIFACTS),
layoutInfoDir = artifacts.getFinalProduct(getLayoutInfoArtifactType(variantScope)),
classLogDir = artifacts.getFinalProduct(DATA_BINDING_BASE_CLASS_LOG_ARTIFACT),
baseFeatureInfoDir = artifacts.getFinalProduct(
FEATURE_DATA_BINDING_BASE_FEATURE_INFO
),
featureInfoDir = artifacts.getFinalProduct(FEATURE_DATA_BINDING_FEATURE_INFO),
aarOutDir = artifacts.getFinalProduct(InternalArtifactType.DATA_BINDING_ARTIFACT),
exportClassListOutFile = variantScope.generatedClassListOutputFileForDataBinding
.takeIf { variantData.type.isExportDataBindingClassList },
enableDebugLogs = enableDebugLogs,
printEncodedErrorLogs = printEncodedErrorLogs,
isTestVariant = variantData.type.isTestComponent,
isEnabledForTests = globalScope.extension.dataBinding.isEnabledForTests,
isEnableV2 = true
)
}
/**
* Returns the module type of a variant. If it is a testing variant, return the module type
* of the tested variant.
*/
@JvmStatic
fun getModuleType(variantScope: VariantScope): CompilerArguments.Type {
val variantData = if (variantScope.variantData.type.isTestComponent) {
variantScope.testedVariantData!!
} else {
variantScope.variantData
}
return if (variantData.type.isAar) {
CompilerArguments.Type.LIBRARY
} else {
if (variantData.type.isBaseModule) {
CompilerArguments.Type.APPLICATION
} else {
CompilerArguments.Type.FEATURE
}
}
}
/**
* Returns the appropriate artifact type of the layout info directory so that it does not
* trigger unnecessary computations (see bug 133092984 and 110412851).
*/
@JvmStatic
fun getLayoutInfoArtifactType(variantScope: VariantScope): InternalArtifactType<Directory> {
return if (variantScope.variantData.type.isAar) {
DATA_BINDING_LAYOUT_INFO_TYPE_PACKAGE
} else {
DATA_BINDING_LAYOUT_INFO_TYPE_MERGE
}
}
}
}
参考
DataBindingCompilerArguments.kt
kotlin.KotlinNullPointerException (no error message) while building APK using Jenkins