参考资料
https://developer.android.goo...
Android Gradle 多项目构建
Android 项目一般分为库项目、应用项目、测试项目,对应的插件是com.android.library、com.android.application、com.android.test
应用项目一般只有一个,最终打包成一个APK,库项目可以有多个,可以被应用项目引用。
多项目设置
在Gradle中可以创建多个项目,并且可以通过文件夹管理,最终在settings.gradle里配置就可以。
// 项目结构
MyProject
+ app
+ libraries
+ lib1
+ lib2
settings.gradle
上面项目结构中,一个根项目MyProject,并有一个settings.gradle配置文件,Sub Project有一个应用项目 App,两个库项目 lib1 和 lib2 放在libraries文件夹下。
在settings.gradle文件中配置
// void include(String[] projectPaths);
include ':app', ':libraries:lib1', ':libraries:lib1:lib2'
如果项目路径很多,可以下面方式指定配置
include ':example1'
project(":example1").projectDir = new File(rootDir, 'chapter/example1')
库项目引用和配置
库项目引用通过dependencies实现。Android Lib打包生成的是aar包,Java Lib打包生成的是jar包,aar包可以有res资源。
dependencies {
implementation project(':libraries:lib1')
}
引用Android库项目,其实就是引用库项目发布的aar包。 默认Android库项目发布都是release版本,可以配置修改默认发布
android {
// 配置 发布debug版本的aar包
defaultPublishConfig "debug"
// 如果配置多个flavor,可以配置flavor + buildtype
// defaultPublishConfig "flavorDebug"
}
发布多个版本aar,默认情况下,是不能同时发布多个arr包,但是可以开启
android {
// 告诉Android Gradle插件,可同时发布不同的aar包
publishNonDefault true
}
其它项目就可以引用不同的aar
dependencies {
flavor1Implementation project(path: ':lib1', configuration: 'flavor1Release')
flavor2Implementation project(path: ':lib1', configuration: 'flavor2Release')
}
发布aar包到Maven中心库
- 在build.gradle文件中应用Maven插件
apply plugin: 'com.android.library'
// 应用Maven仓库
apply plugin: 'maven'
- 配置Maven构建三要素,分别是group:artifact:version
// build.gradle
apply plugin: 'com.android.library'
// 应用Maven仓库
apply plugin: 'maven
group = 'com.custom.plugin'
version = '1.0.2'
为了更好的联调测试,提供快照版本SNAPSHOT,如:配置成1.0.0-SNAPSHOT。发布到snapshot中心库时,每次发布版本号不会变化,只会在版本号后按顺序号+1,如:1.0.0-1,1.0.0-2,1.0.0-3等。引用时版本号写成1.0.0-SNAPSHOT即可,Maven会自动下载最新版本快照。
- 发布配置,如:发布哪个Maven仓库,使用的用户名和密码,发布什么格式的存档,artifact是什么等
boolean needUploadToLocal = false;//是否将Library发布到本地
boolean isArchivesRelease = false;//是否将Library发布到Release仓库;false 为发布到SnapShot仓库
//gradlew :sub-project:newsindiasdk:clean :sub-project:newsindiasdk:uploadArchives
//com.cmcm.onews.sdk:onews_sdk:5.3.1.12-SNAPSHOT@aar
apply plugin: 'maven'
//注意了,以后maven帐户请在local.properties里配置,eg:
//maven.u= your user
//maven.p= your pwd
Properties props = new Properties()
props.load(new FileInputStream(project.rootProject.file("local.properties")))
String u = props.get('maven.u');
String p = props.get('maven.p');
uploadArchives {
repositories {
mavenDeployer {
if (needUploadToLocal) {
pom.version = "Debug"
repository(url: "D:/NewsArch")
} else {
pom.version = "6.3.1.3"
if (isArchivesRelease) {
repository(url: "http://10.60.80.74:8081/nexus/content/repositories/cleanmasterrelease") {
authentication(userName: u, password: p)
}
} else {
pom.version += "-SNAPSHOT" // -SNAPSHOT
repository(url: "http://10.60.80.74:8081/nexus/content/repositories/cleanmastersnapshot") {
authentication(userName: u, password: p)
}
}
}
pom.artifactId = "onews_sdk"
pom.groupId = "com.cmcm.onews.sdk"
}
}
}
- 使用它们需要配置仓库,因为是私有仓库,使用时告诉Gradle
// Root Project中build.gradle
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
}
}
allprojects {
repositories {
jcenter()
// 发布版本
maven {
url "http://10.60.80.74:8081/nexus/content/repositories/cleanmasterrelease"
}
// 快照版本
maven {
url "http://10.60.80.74:8081/nexus/content/repositories/cleanmastersnapshot"
}
}
}
// Sub Project中添加依赖
dependencies {
implementation "com.cmcm.onews.sdk:onews_sdk:6.2.4.7-SNAPSHOT@aar"
}
Android Gradle 多渠道构建
多渠道构建基本原理
在Android Gradle中,有一个Build Variant概念,翻译就是构建变体,构建的产物(APK)。
Build Variant = Project Flavor + Build Type
Build Type 构建类型,如:Release,Debug;Project Flavor 构建渠道,如:Baidu,Google。
Build Variant 构建变体,如:baiduRelease,baiduDebug,googleRelease,googleDebug
Android Gradle提供productFlavors 方法添加不同的渠道,参数接受域对象类型,ProductFlavor作为闭包参数
android {
productFlavors {
baidu {}
google {}
}
}
配置发布渠道后,Android Gradle就会产生很多Task,基本上都是基于 Project Flavor + Build Type方式生成的,如:assembleBaidu,assembleRelease,assembleBaiduRelease。assemble开头的负责生成构建产物APK。
每个Project Flavor,也就是每个渠道,可以定义自己的SourceSet,Dependencies依赖。
Flurry多渠道 和 友盟多渠道 构建
- Flurry多渠道配置
Flurry的统计是已Application划分渠道的,每个Application都有一个key。在Flurry上创建Application时自动生成,可以为每个渠道配置不同的Flurry Key,使用BuildConfig配置。
android {
productFlavors {
baidu {
buildConfigField 'String', 'FLURRY_KEY', "\"QHHJNNGGHJK\""
}
google {
buildConfigField 'String', 'FLURRY_KEY', "\"kkkiihhhgggv\""
}
}
}
...
Flurry.init(this, BuildConfig.FLURRY_KEY);
- 友盟多渠道配置
友盟存在渠道概念,但它不是在代码中指定的,而是在AndroidManifest.xml文件中配置的,通过配置meta-data标签来设置。
<meta-data
android:name="UMENG_CHANNEL"
android:value="Channel ID" />
Channel ID就是渠道值,如:Baidu,Google。可通过manifestPlaceholders来动态改变渠道值。
多渠道构建定制
通过配置Android Gradle 插件的 ProductFlavor可灵活控制每个渠道包.
- applicationId
它是ProductFlavor属性,设置该渠道的包名,想为渠道设置特别的包名,可以使用applicationId这个属性设置
android {
productFlavors {
baidu {
applicationId "com.gradle.test.baidu"
}
}
}
- consumerProguardFiles
即是一个属性,也有一个同名的方法,只对Android库项目有用。consumerProguardFiles方法是一直添加,不会清空之前的混淆文件,而consumerProguardFiles属性方式每次都是新的混淆文件列表,以前的配置会先被清空。
// 属性
public void setConsumerProguardFiles(@NonNull Iterable<?> proguardFileIterable) {
getConsumerProguardFiles().clear();
consumerProguardFiles(Iterables.toArray(proguardFileIterable, Object.class));
}
// 方法
public void consumerProguardFiles(@NonNull Object... proguardFiles) {
for (Object proguardFile : proguardFiles) {
consumerProguardFile(proguardFile);
}
}
当发布库项目生成AAR时,使用consumerProguardFiles配置的混淆文件也会被打包到AAR里一起发布,当应用项目引用这个AAR时,并启动混淆时,会自动使用AAR包里的混淆文件对AAR包里代码进行混淆,就不用对AAR包进行混淆配置,因为AAR自带了。
android {
productFlavors {
baidu {
consumerProguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
- manifestPlaceholders
- multiDexEnabled
启动多个dex配置,用来突破65535方法问题。
- proguardFiles
配置混淆文件
- signingConfig
配置签名
- testApplicationId
是一个属性,用来适配测试包的包名,值一般为App的包名+.test
android {
productFlavors {
baidu {
testApplicationId "com.gradle.test"
}
}
}
- testFunctionalTest 和 testHandleProfiling
testFunctionalTest 表示是否为功能测试,testHandleProfiling表示是否启动分析功能。
android {
productFlavors {
baidu {
testFunctionalTest true
testHandleProfiling true
}
}
}
- testInstrumentationRunner
配置运行测试使用的 Instrumentation Runner 的全路径的类名,且必须是android.app.Instrumentation 的子类。
android {
productFlavors {
baidu {
testInstrumentationRunner "android.test.InstrumentationTestRunner"
}
}
}
- testInstrumentationRunnerArguments
与testInstrumentationRunner一起使用,配置 Instrumentation Runner 使用的参数,最终使用的都是adb shell am instrument 这个命令。testInstrumentationRunnerArguments 参数被转换传递给 am instrument这个命令使用,如:-e key value
android {
productFlavors {
baidu {
testInstrumentationRunnerArguments.put("converage", 'true')
}
}
}
- versionName 和 versionCode
配置渠道的版本号和版本名称
android {
productFlavors {
baidu {
versionName "2.1.5"
versionCode 215
}
}
}
- dimension
为了基于不同标准构建App,可以通过dimension 多维度的方式解决。
dimension是ProductFlavor的属性,接受一个字符串,该字符串就是维度名称,作为ProductFlavor的维度。维度名称不是随意指定的,在使用前需要声明,可以通过flavorDimensions 方法声明。
android {
// 声明维度后,才能在productFlavors中使用
// flavorDimensions 可同时指定多个维度,但是维度有顺序优先级的,第一个优先级最大
flavorDimensions 'api', 'version'
productFlavors {
demo {
dimension 'version'
...
}
full {
dimension 'version'
...
}
minApi24 {.
dimension 'api'
minSdkVersion '24'
versionNameSuffix "-minApi24"
...
}
minApi21 {
dimension "api"
minSdkVersion '21'
versionNameSuffix "-minApi21"
...
}
}
}
上例中,最后生成的variant(构建变体)会被几个 ProductFlavor对象配置:
- Android中的defaultConfig配置,也是一个ProductFlavor
- api维度的ProductFlavor,被dimension 配置标记为api的ProductFlavor
- version维度的ProductFlavor, 被dimension 配置标记为version的ProductFlavor
维度优先级很重要,高优先级的flavor会替换掉低优先级的资源、代码、配置等,上例中优先级:api>version>defaultConfig
通过dimension 指定维度后,Android Gradle会帮助生成相应 Task、SourceSet、Dependencies等。现在构建变体的产物=Api+Version+BuildType, 如:MinApi21DemoRelease、MinApi21FullRelease、MinApi21DemoDebug、MinApi21FullDebug等
提供多渠道构建的效率
生成多个渠道包主要是为了跟踪每个渠道的情况,如:新增、活跃、留存。除了根据渠道号区分每个渠道外,大部分情况下没有什么不同,唯一区别是属于哪个渠道。
因为Android Gradle对每个渠道包都要执行构建过程,导致速度变慢。美团研究一个办法,在APK的MEAT-INF目录下添加空文件不用重新签名原理。
- 利用Android Gradle打一个基本包(母包)
- 基于母包复制一个,文件名要区分产品,打包时间,版本,渠道
- 对复制的APK进行修改,在META-INF目录下新增空文件,文件名必须要区分渠道,如:mtchannel_google
- 利用python脚本执行 2, 3 步骤操作
使用时,在APK启动(Application onCreate)读取APK中META-INF目录下的前缀为mtchannel_文件,如果找到,把文件名取出来,然后就可以得到渠道标识(google)了,美团实现的代码:
public static String getChannel(Context context) {
ApplicationInfo appinfo = context.getApplicationInfo();
String sourceDir = appinfo.sourceDir;
String ret = "";
ZipFile zipfile = null;
try {
zipfile = new ZipFile(sourceDir);
Enumeration<?> entries = zipfile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = ((ZipEntry) entries.nextElement());
String entryName = entry.getName();
if (entryName.startsWith("mtchannel")) {
ret = entryName;
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (zipfile != null) {
try {
zipfile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
String[] split = ret.split("_");
if (split != null && split.length >= 2) {
return ret.substring(split[0].length() + 1);
} else {
return "";
}
}
利用python脚本批处理,向APK中META-INF目录写入渠道文件,文件名前缀为mtchannel_
import zipfile
zipped = zipfile.ZipFile(your_apk, 'a', zipfile.ZIP_DEFLATED)
empty_channel_file = "META-INF/mtchannel_{channel}".format(channel=your_channel)
zipped.write(your_empty_file, empty_channel_file)
然后就是配置渠道列表,下载AndroidMultiChannelBuildTool工程后,在PythonTool/Info/channel.txt文件中添加渠道,渠道以换行隔开。
将想要批量打包的apk文件拷贝到PythonTool目录下(与MultiChannelBuildTool.py同级),运行py脚本即可打包完成。(生成的渠道apk包在output_** 目录下)
参考资料:
https://www.cnblogs.com/ct201...
https://github.com/GavinCT/An...
https://github.com/Meituan-Di...
Android Gradle 测试
Android为测试程序提供了很好支持,既可以使用传统的JUnit测试,又可以使用Android提供的Instrument测试。
基本概念
使用Android Studio新建一个项目时,会帮助我们默认生成 main 和 androidTest SourceSet。运行测试时,androidTest SourceSet会被构建成一个可以安装到设备上测试的APK,这个测试APK中有写好的测试用例,会被执行来测试APP。
在androidTest SourceSet中可以依赖各种测试库,如:单元测试的,集成测试的,espresso UI测试的,uiautomator自动化测试的。
一般测试APK会统一配置,而不是针对每个渠道都配置,在defaultConfig对测试APK配置后,会自动生成所需的包名、AndroidManifest.xml文件等信息。
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
testApplicationId "com.example.myapplication.test"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
testFunctionalTest true
testHandleProfiling true
}
}
根据配置自动生成AndroidManifest.xml文件,android:targetPackage是Android自动生成的。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication.test" >
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="29" />
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:functionalTest="true"
android:handleProfiling="true"
android:label="Tests for com.example.myapplication"
android:targetPackage="com.example.myapplication" />
<application android:debuggable="true" >
<uses-library android:name="android.test.runner" />
</application>
</manifest>
也可以在androidTest中配置依赖,正式APK不会编译到APK中,只有Android测试的时候才会被编译到测试APK中。
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
默认情况下测试APK主要在debug模式下,debug模式下不会混淆代码,有利于发现问题,且对测试代码覆盖率也有帮助。Android Gradle提供了testBuildType,可以修改BuildType
android {
// 修改测试的是release类型的apk,默认类型是debug
testBuildType 'release'
}
怎么运行写好的测试代码呢?
使用 gradlew connectedCheck 任务来运行测试。这个任务是一个引导性的任务,首先使用assembleAndroidTest 和 assembleDebug 任务构建测试应用 和 被测试应用,然后通过*install任务安装这两个应用,再运行写好的测试代码,运行完后,卸载这两个应用。
最后测试结果会保存在build/reports/androidTests/connected目录下,可以通过浏览器查看index.html测试结果。
本地单元测试
这种测试和原生的java测试一样,不依赖android框架或只有非常少的依赖,直接运行在本地开发机器上,不需要运行在Android设备上。但有时也需要Android框架本身一些代码依赖,如:Context,可以使用模拟框架来模拟这种依赖关系,如:Mockito 和 JMock
AndroidTest测试有自己的SourceSet目录 src/androidTest/java; 对于本地单元测试也有自己的目录src/test/java,测试用例用来测试main这个SourceSet代码。
Android本地单元测试,也使用JUnit这个流行的测试框架测试
dependencies {
// JUnit3的测试用例需要集成junit.framework.TestCase,且测试方法要以test为前缀
// JUnit4只需要使用 @Test 注解标记就可以,推荐使用JUnit4
testImplementation 'junit:junit:4.12'
编写好测试用例后, 运行 gradlew test 任务可以运行所有的单元测试用例,然后在build/reports/tests目录下生成测试报告。
如果想运行debug模式下的使用gradlew testDebugUnitTest任务。
在执行test任务时,如果想依赖Android框架,只能使用模拟对象框架,如:Mockito ,版本要是1.9.5以上,与Android单元测试兼容。
dependencies {
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-all:1.10.19'
}
编写需要测试的代码,需要使用Context
public class Utils {
private Context mContext;
public Utils(Context context) {
this.mContext = context;
}
public String getAppName(){
return mContext.getString(R.string.app_name);
}
}
如果要测试上面的代码,因为需要一个Context,就要使用Mockito来模拟Context
import android.content.Context;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
/**
* kerwin <xujinbing839@pingan.com.cn> 2019-09-11
*/
@RunWith(MockitoJUnitRunner.class)
public class UtilsTest {
private static final String APP_NAME = "MyApplication";
@Mock
Context mMockContext;
@Test
public void readAppNameFromContext() {
when(mMockContext.getString(R.string.app_name)).thenReturn(APP_NAME);
Utils utils = new Utils(mMockContext);
String appName = utils.getAppName();
assertThat(appName, is(APP_NAME));
}
}
首先要告诉JUnit4,要使用MockitoJUnitRunner这个单元测试的运行者来执行,不然 @Mock 注解就不认识了。使用@Mock 注解模拟一个Context对象,mMockContext就是被Mockito模拟出来的。
when逻辑需要和Utils里的getAppName方法逻辑一样,然后使用thenReturn告诉模拟期望返回的值
使用 gradlew test 执行任务,查看报告结果。
参考文献
https://static.javadoc.io/org...
Instrument测试
Instrument测试是基于Android设备或模拟器的测试,是一种高模拟和仿真测试。它可以使用Android SDK框架的所有类和特性,如:Context。还提供了Instrumenttation类,可以很方便的获得测试APK的Context、Activity。且可以使用Instrument测试做单元测试、UI自动化测试、集成测试。
Instrument测试要生成一个测试的APK,所以要对测试APK配置。testInstrumentationRunner 这个runner可以编写基于JUnit4测试用例,且可搭配使用JUnit4新特性。
android {
defaultConfig {
// 指定生成测试APK的包名,默认:被测试APK包名+test
testApplicationId "com.example.myapplication.test"
// 配置使用Runner
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
}
// 添加依赖
dependencies {
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.2.0'
// Optional
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
// Optional
androidTestImplementation 'org.hamcrest:hamcrest-library:2.1'
// Optional
androidTestImplementation 'androidx.test.uiautomator:uiautomator-v18:2.2.0-alpha1'
}
rules库,为测试定义一些规则,实现自JUnit的rule,可以对JUnit扩展。如:ActivityTestRule指定要测试的Activity。编写好测试用例后,运行gradlew connectedAndroidTest 执行所有Instrument测试,在build/reports/androidTests目录下查看报告.
import android.util.Log;
import androidx.test.filters.LargeTest;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
// LargeTest标记,说明有更高的权限,如多线程、访问数据库、时间限制也更长
@LargeTest
public class ExampleInstrumentedTest {
// 指定规则,测试MainActivity
@Rule
public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
@Before
public void init() {
Log.d("kerwin_test", "init :::" + Thread.currentThread().getName());
// init :::Instr: androidx.test.runner.AndroidJUnitRunner
}
@Test
public void valid() throws Throwable {
mActivityRule.runOnUiThread(new Runnable() {
@Override
public void run() {
mActivityRule.getActivity().findViewById(R.id.text1).performClick();
}
});
}
}
测试选项配置
Android Gradle插件提供testOptions { } ,可对测试进行配置,如:生成测试报告的目录。
TestOptions提供配置项:
- resultsDir 是一个属性,配置生成测试结果目录
- reportDir 是一个属性,配置生成测试报告目录
- unitTests 即是属性,也是一个闭包,控制单元测试的执行
android {
testOptions {
resultsDir "${project.buildDir}/myResults"
reportDir "${project.buildDir}/myReports"
}
}
单个项目,测试报告可以生成在指定的目录下,有多个项目怎么办呢?
比如引用了多个库项目,每个库项目也有自己的测试,生成自己的报告,这样比较分散,不容易查看,如果统一起来查看就方便了。Android 提供了另一个插件 android-reporting ,应用后新增一个名为 mergeAndroidReports 任务,执行完测试后调用即可。
// 在Root Project中的build.gradle 文件最后应用后,添加的任务也在Root项目中。
apply plugin: 'android-reporting'
然后执行 gradlew deviceCheck mergeAndroidReports --continue 任务。mergeAndroidReports合并报告,--continue在测试失败的时候,也继续执行其他测试用例,一直执行完成为止。合并后的报告在Root项目的build目录中。
unitTests配置,对应的类型是UnitTestOptions,它是所有测试任务的一个集合。UnitTestOptions 对象有一个Test类型的域对象集合DomainObjectSet。对应源码:
public static class UnitTestOptions {
private DomainObjectSet<Test> testTasks = new DefaultDomainObjectSet<Test>(Test.class);
public void all(final Closure<Test> configClosure) {
testTasks.all(
new Action<Test>() {
@Override
public void execute(Test testTask) {
ConfigureUtil.configure(configClosure, testTask);
}
});
}
}
all方法可以遍所有的Test,它是Task类型。可以对他们做一些配置,或者根据任务做一些判断等。
android {
testOptions {
unitTests.all {
println "testName: >>>>>>${it.name}"
}
}
}
代码覆盖率
有了测试用例,就要有相应的测试代码覆盖率统计,这样才能知道代码是否被测试用例完全覆盖,还有哪些没有覆盖到,如何进行补全测试用例。Android Gradle内置了代码覆盖lv的报告生成,默认是关闭的。
android {
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
testCoverageEnabled true
}
}
}
testCoverageEnabled用于控制代码覆盖率统计是否开启,它是BuildType的一个属性,true代表开启,默认是false。
运行 gradlew createDebugCoverageReport 任务后,就会自动执行测试用例,并生成测试代码覆盖率报告,报告路径build/reports/coverage
Lint支持
Android提供了针对代码、资源优化工具Lint。可帮助检查哪些资源没有被使用,哪些使用了新的API,哪些资源没有国际化等,并生成一份报告,告诉哪些需要优化。
运行 gradle lint 任务即可生成报告,默认生成报告在build/reports/lint-results.html
Lint是一个命令行工具,在Android Tools目录下。在Android Gradle插件提供了 lintOptions {}这个闭包配置Lint。
android {
lintOptions {
// 遇到错误终止构建
abortOnError true
// 警告也会被当前错误处理
warningsAsErrors true
// 需要检查是否使用了新的API
check 'NewApi'
}
}
1. abortOnError
是一个属性,接受boolean类型值,配置Lint发现错误时是否退出Gradle构建。默认true
2. absolutePaths
是一个属性,接受boolean类型值,配置错误的输出里是否应该显示绝对路径,默认true,显示相对路径
3. check
是一个属性,也是一个方法,配置哪些项目需要Lint检查,这个项目就是Issue Id(s)。
NewApi这个就是一个issue id,lint有很多可用的issue id,通过lint --list可以查看可用的id。冒号前面是id,后面是对这个issue id的说明。可以使用lint --show命令查看详细说明.
Valid issue categories:
Correctness
Correctness:Messages
Correctness:Chrome OS
Security
Performance
Usability:Typography
Usability:Icons
Usability
Accessibility
Internationalization
Internationalization:Bidirectional Text
Valid issue id's:
"ContentDescription": Image without contentDescription
"AddJavascriptInterface": addJavascriptInterface Called
"ShortAlarm": Short or Frequent Alarm
"AllCaps": Combining textAllCaps and markup
"AllowAllHostnameVerifier": Insecure HostnameVerifier
"AlwaysShowAction": Usage of showAsAction=always
"InvalidUsesTagAttribute": Invalid name attribute for uses element.
"MissingIntentFilterForMediaSearch": Missing intent-filter with action
android.media.action.MEDIA_PLAY_FROM_SEARCH
"MissingMediaBrowserServiceIntentFilter": Missing intent-filter with action
android.media.browse.MediaBrowserService.
"MissingOnPlayFromSearch": Missing onPlayFromSearch.
"ImpliedTouchscreenHardware": Hardware feature touchscreen not explicitly
marked as optional
"MissingTvBanner": TV Missing Banner
"MissingLeanbackLauncher": Missing Leanback Launcher Intent Filter.
"MissingLeanbackSupport": Missing Leanback Support.
"PermissionImpliesUnsupportedHardware": Permission Implies Unsupported
Hardware
"UnsupportedTvHardware": Unsupported TV Hardware Feature
"SupportAnnotationUsage": Incorrect support annotation usage
"ShiftFlags": Dangerous Flag Constant Declaration
"LocalSuppress": @SuppressLint on invalid element
"SwitchIntDef": Missing @IntDef in Switch
"UniqueConstants": Overlapping Enumeration Constants
"InlinedApi": Using inlined constants on older versions
"Override": Method conflicts with new inherited method
"ObsoleteSdkInt": Obsolete SDK_INT Version Check
"NewApi": Calling new methods on older versions
"UnusedAttribute": Attribute unused on older versions
"AppCompatMethod": Using Wrong AppCompat Method
"AppCompatCustomView": Appcompat Custom Widgets
"AppCompatResource": Menu namespace
"GoogleAppIndexingApiWarning": Missing support for Firebase App Indexing Api
"GoogleAppIndexingWarning": Missing support for Firebase App Indexing
"AppLinksAutoVerifyError": App Links Auto Verification Failure
"AppLinksAutoVerifyWarning": Potential App Links Auto Verification Failure
"AppLinkUrlError": URL not supported by app for Firebase App Indexing
"TestAppLink": Unmatched URLs
"InconsistentArrays": Inconsistencies in array element counts
"Assert": Assertions
"BadHostnameVerifier": Insecure HostnameVerifier
"BatteryLife": Battery Life Issues
"BackButton": Back button
"ButtonCase": Cancel/OK dialog button capitalization
"ButtonOrder": Button order
"ButtonStyle": Button should be borderless
"ByteOrderMark": Byte order mark inside files
"MissingSuperCall": Missing Super Call
"AdapterViewChildren": AdapterViews cannot have children in XML
"ScrollViewCount": ScrollViews can have only one child
"PermissionImpliesUnsupportedChromeOsHardware": Permission Implies Unsupported
Chrome OS Hardware
"UnsupportedChromeOsHardware": Unsupported Chrome OS Hardware Feature
"GetInstance": Cipher.getInstance with ECB
"CommitTransaction": Missing commit() calls
"Recycle": Missing recycle() calls
"CommitPrefEdits": Missing commit() on SharedPreference editor
"ApplySharedPref": Use apply() on SharedPreferences
"ClickableViewAccessibility": Accessibility in Custom Views
"EasterEgg": Code contains easter egg
"StopShip": Code contains STOPSHIP marker
"MissingConstraints": Missing Constraints in ConstraintLayout
"VulnerableCordovaVersion": Vulnerable Cordova Version
"CustomViewStyleable": Mismatched Styleable/Custom View Name
"CutPasteId": Likely cut & paste mistakes
"SimpleDateFormat": Implied locale in date format
"SetTextI18n": TextView Internationalization
"Deprecated": Using deprecated resources
"MissingPrefix": Missing Android XML namespace
"MangledCRLF": Mangled file line endings
"DuplicateIncludedIds": Duplicate ids across layouts combined with include
tags
"DuplicateIds": Duplicate ids within a single layout
"DuplicateDefinition": Duplicate definitions of resources
"ReferenceType": Incorrect reference types
"StringEscaping": Invalid string escapes
"UnpackedNativeCode": Missing android:extractNativeLibs=false
"UnsafeDynamicallyLoadedCode": load used to dynamically load code
"UnsafeNativeCodeLocation": Native code outside library directory
"EllipsizeMaxLines": Combining Ellipsize and Maxlines
"ExifInterface": Using android.media.ExifInterface
"ExtraText": Extraneous text in resource files
"FieldGetter": Using getter instead of field
"InvalidAnalyticsName": Invalid Analytics Name
"MissingFirebaseInstanceTokenRefresh": Missing Firebase Instance ID Token
Refresh
"FontValidationError": Validation of font files
"FontValidationWarning": Validation of font files
"FullBackupContent": Valid Full Backup Content File
"ValidFragment": Fragment not instantiatable
"GetContentDescriptionOverride": Overriding getContentDescription() on a View
"PackageManagerGetSignatures": Potential Multiple Certificate Exploit
"AccidentalOctal": Accidental Octal
"UseOfBundledGooglePlayServices": Use of bundled version of Google Play
services
"GradleCompatible": Incompatible Gradle Versions
"GradleDependency": Obsolete Gradle Dependency
"GradleDeprecated": Deprecated Gradle Construct
"DevModeObsolete": Dev Mode Obsolete
"DuplicatePlatformClasses": Duplicate Platform Classes
"GradleGetter": Gradle Implicit Getter Call
"GradlePluginVersion": Incompatible Android Gradle Plugin
"HighAppVersionCode": VersionCode too high
"GradleIdeError": Gradle IDE Support Issues
"GradlePath": Gradle Path Issues
"GradleDynamicVersion": Gradle Dynamic Version
"NotInterpolated": Incorrect Interpolation
"StringShouldBeInt": String should be int
"NewerVersionAvailable": Newer Library Versions Available
"MinSdkTooLow": API Version Too Low
"GridLayout": GridLayout validation
"HandlerLeak": Handler reference leaks
"HardcodedDebugMode": Hardcoded value of android:debuggable in the manifest
"HardcodedText": Hardcoded text
"HardwareIds": Hardware Id Usage
"IconDuplicatesConfig": Identical bitmaps across various configurations
"IconDuplicates": Duplicated icons under different names
"GifUsage": Using .gif format for bitmaps is discouraged
"IconColors": Icon colors do not follow the recommended visual style
"IconDensities": Icon densities validation
"IconDipSize": Icon density-independent size validation
"IconExpectedSize": Icon has incorrect size
"IconExtension": Icon format does not match the file extension
"IconLauncherShape": The launcher icon shape should use a distinct silhouette
"IconLocation": Image defined in density-independent drawable folder
"IconMissingDensityFolder": Missing density folder
"IconMixedNinePatch": Clashing PNG and 9-PNG files
"IconNoDpi": Icon appears in both -nodpi and dpi folders
"IconXmlAndPng": Icon is specified both as .xml file and as a bitmap
"ConvertToWebp": Convert to WebP
"WebpUnsupported": WebP Unsupported
"IncludeLayoutParam": Ignored layout params on include
"DisableBaselineAlignment": Missing baselineAligned attribute
"InefficientWeight": Inefficient layout weight
"NestedWeights": Nested layout weights
"Orientation": Missing explicit orientation
"Suspicious0dp": Suspicious 0dp dimension
"InstantApps": Instant App Issues
"DuplicateDivider": Unnecessary Divider Copy
"TrustAllX509TrustManager": Insecure TLS/SSL trust manager
"InvalidImeActionId": Invalid imeActionId declaration
"InvalidPackage": Package not included in Android
"DrawAllocation": Memory allocations within drawing code
"UseSparseArrays": HashMap can be replaced with SparseArray
"UseValueOf": Should use valueOf instead of new
"JavascriptInterface": Missing @JavascriptInterface on methods
"JobSchedulerService": JobScheduler problems
"KeyboardInaccessibleWidget": Keyboard inaccessible widget
"LabelFor": Missing labelFor attribute
"InconsistentLayout": Inconsistent Layouts
"InflateParams": Layout Inflation without a Parent
"StaticFieldLeak": Static Field Leaks
"DefaultLocale": Implied default locale in case conversion
"LocaleFolder": Wrong locale name
"GetLocales": Locale crash
"InvalidResourceFolder": Invalid Resource Folder
"WrongRegion": Suspicious Language/Region Combination
"UseAlpha2": Using 3-letter Codes
"LogConditional": Unconditional Logging Calls
"LongLogTag": Too Long Log Tags
"LogTagMismatch": Mismatched Log Tags
"AllowBackup": AllowBackup/FullBackupContent Problems
"MissingApplicationIcon": Missing application icon
"DeviceAdmin": Malformed Device Admin
"DuplicateActivity": Activity registered more than once
"DuplicateUsesFeature": Feature declared more than once
"GradleOverrides": Value overridden by Gradle build script
"IllegalResourceRef": Name and version must be integer or string, not
resource
"MipmapIcons": Use Mipmap Launcher Icons
"MockLocation": Using mock location provider in production
"MultipleUsesSdk": Multiple <uses-sdk> elements in the manifest
"ManifestOrder": Incorrect order of elements in manifest
"MissingVersion": Missing application name/version
"OldTargetApi": Target SDK attribute is not targeting latest version
"UniquePermission": Permission names are not unique
"UsesMinSdkAttributes": Minimum SDK and target SDK attributes not defined
"WearableBindListener": Usage of Android Wear BIND_LISTENER is deprecated
"WrongManifestParent": Wrong manifest parent
"InvalidPermission": Invalid Permission Attribute
"ManifestResource": Manifest Resource References
"ManifestTypo": Typos in manifest tags
"FloatMath": Using FloatMath instead of Math
"MergeMarker": Code contains merge marker
"MergeRootFrame": FrameLayout can be replaced with <merge> tag
"IncompatibleMediaBrowserServiceCompatVersion": Obsolete version of
MediaBrowserServiceCompat
"InnerclassSeparator": Inner classes should use $ rather than .
"Instantiatable": Registered class is not instantiatable
"MissingRegistered": Missing registered class
"MissingId": Fragments should specify an id or tag
"LibraryCustomView": Custom views in libraries should use res-auto-namespace
"ResAuto": Hardcoded Package in Namespace
"NamespaceTypo": Misspelled namespace declaration
"UnusedNamespace": Unused namespace
"NegativeMargin": Negative Margins
"NestedScrolling": Nested scrolling widgets
"NetworkSecurityConfig": Valid Network Security Config File
"MissingBackupPin": Missing Backup Pin
"PinSetExpiry": Validate <pin-set> expiration attribute
"NfcTechWhitespace": Whitespace in NFC tech lists
"UnlocalizedSms": SMS phone number missing country code
"ObjectAnimatorBinding": Incorrect ObjectAnimator Property
"AnimatorKeep": Missing @Keep for Animated Properties
"ObsoleteLayoutParam": Obsolete layout params
"OnClick": onClick method does not exist
"Overdraw": Overdraw: Painting regions more than once
"DalvikOverride": Method considered overridden by Dalvik
"OverrideAbstract": Not overriding abstract methods on older platforms
"ParcelCreator": Missing Parcelable CREATOR field
"UnusedQuantity": Unused quantity translations
"MissingQuantity": Missing quantity translation
"ImpliedQuantity": Implied Quantities
"ExportedPreferenceActivity": PreferenceActivity should not be exported
"PrivateApi": Using Private APIs
"PackagedPrivateKey": Packaged private key
"PrivateResource": Using private resources
"ProguardSplit": Proguard.cfg file contains generic Android rules
"Proguard": Using obsolete ProGuard configuration
"PropertyEscape": Incorrect property escapes
"UsingHttp": Using HTTP instead of HTTPS
"SpUsage": Using dp instead of sp for text sizes
"InOrMmUsage": Using mm or in dimensions
"PxUsage": Using 'px' dimension
"SmallSp": Text size is too small
"ParcelClassLoader": Default Parcel Class Loader
"PendingBindings": Missing Pending Bindings
"RecyclerView": RecyclerView Problems
"Registered": Class is not registered in the manifest
"RelativeOverlap": Overlapping items in RelativeLayout
"RequiredSize": Missing layout_width or layout_height attributes
"AaptCrash": Potential AAPT crash
"ResourceCycle": Cycle in resource definitions
"ResourceName": Resource with Wrong Prefix
"ValidRestrictions": Invalid Restrictions Descriptor
"RtlCompat": Right-to-left text compatibility issues
"RtlEnabled": Using RTL attributes without enabling RTL support
"RtlSymmetry": Padding and margin symmetry
"RtlHardcoded": Using left/right instead of start/end attributes
"ScrollViewSize": ScrollView size validation
"SdCardPath": Hardcoded reference to /sdcard
"SecureRandom": Using a fixed seed with SecureRandom
"TrulyRandom": Weak RNG
"ExportedContentProvider": Content provider does not require permission
"ExportedReceiver": Receiver does not require permission
"ExportedService": Exported service does not require permission
"SetWorldReadable": File.setReadable() used to make file world-readable
"SetWorldWritable": File.setWritable() used to make file world-writable
"GrantAllUris": Content provider shares everything
"WorldReadableFiles": openFileOutput() or similar call passing
MODE_WORLD_READABLE
"WorldWriteableFiles": openFileOutput() or similar call passing
MODE_WORLD_WRITEABLE
"ServiceCast": Wrong system service casts
"WifiManagerLeak": WifiManager Leak
"WifiManagerPotentialLeak": WifiManager Potential Leak
"SetJavaScriptEnabled": Using setJavaScriptEnabled
"SignatureOrSystemPermissions": signatureOrSystem permissions declared
"SQLiteString": Using STRING instead of TEXT
"SSLCertificateSocketFactoryCreateSocket": Insecure call to
SSLCertificateSocketFactory.createSocket()
"SSLCertificateSocketFactoryGetInsecure": Call to
SSLCertificateSocketFactory.getInsecure()
"StateListReachable": Unreachable state in a <selector>
"AuthLeak": Code might contain an auth leak
"StringFormatCount": Formatting argument types incomplete or inconsistent
"StringFormatMatches": String.format string doesn't match the XML format
string
"StringFormatInvalid": Invalid format string
"PluralsCandidate": Potential Plurals
"UseCheckPermission": Using the result of check permission calls
"CheckResult": Ignoring results
"ResourceAsColor": Should pass resolved color instead of resource id
"MissingPermission": Missing Permissions
"Range": Outside Range
"ResourceType": Wrong Resource Type
"RestrictedApi": Restricted API
"WrongThread": Wrong Thread
"WrongConstant": Incorrect constant
"VisibleForTests": Visible Only For Tests
"ProtectedPermissions": Using system app permission
"TextFields": Missing inputType or hint
"TextViewEdits": TextView should probably be an EditText instead
"SelectableText": Dynamic text should probably be selectable
"MenuTitle": Missing menu title
"ShowToast": Toast created but not shown
"TooDeepLayout": Layout hierarchy is too deep
"TooManyViews": Layout has too many views
"ExtraTranslation": Extra translation
"MissingTranslation": Incomplete translation
"Typos": Spelling error
"TypographyDashes": Hyphen can be replaced with dash
"TypographyEllipsis": Ellipsis string can be replaced with ellipsis character
"TypographyFractions": Fraction string can be replaced with fraction
character
"TypographyOther": Other typographical problems
"TypographyQuotes": Straight quotes can be replaced with curvy quotes
"UnsafeProtectedBroadcastReceiver": Unsafe Protected BroadcastReceiver
"UnprotectedSMSBroadcastReceiver": Unprotected SMS BroadcastReceiver
"UnusedResources": Unused resources
"UnusedIds": Unused id
"UseCompoundDrawables": Node can be replaced by a TextView with compound
drawables
"UselessLeaf": Useless leaf layout
"UselessParent": Useless parent layout
"EnforceUTF8": Encoding used in resource files is not UTF-8
"VectorRaster": Vector Image Generation
"VectorDrawableCompat": Using VectorDrawableCompat
"VectorPath": Long vector paths
"InvalidVectorPath": Invalid vector paths
"ViewConstructor": Missing View constructors for XML inflation
"ViewHolder": View Holder Candidates
"ViewTag": Tagged object leaks
"WrongViewCast": Mismatched view type
"FindViewByIdCast": Add Explicit Cast
"Wakelock": Incorrect WakeLock usage
"WakelockTimeout": Using wakeLock without timeout
"InvalidWearFeatureAttribute": Invalid attribute for Wear uses-feature
"WearStandaloneAppFlag": Invalid or missing Wear standalone app flag
"WebViewLayout": WebViews in wrap_content parents
"WrongCall": Using wrong draw/layout method
"WrongCase": Wrong case for view tag
"InvalidId": Invalid ID declaration
"NotSibling": RelativeLayout Invalid Constraints
"UnknownId": Reference to an unknown id
"UnknownIdInLayout": Reference to an id that is not in the current layout
"SuspiciousImport": 'import android.R' statement
"WrongFolder": Resource file in the wrong res folder
"WrongThreadInterprocedural": Wrong Thread (Interprocedural)
4. checkAllWarnings
是一个属性,接受boolean类型值,true表示需要检查所有警告的issue,包括默认被关闭的issue;false不检查
5. checkReleaseBuilds
是一个属性,接受boolean类型值,配置在release构建的过程中,Lint 是否检查致命的错误问题,默认true,一旦发现有fatal级别的问题,release构建就会终止。
6. disable
用来关闭给定issue ids的Lint检查,参数接受是issue id。
7. enable
与disable相反
8. explainIssues
是一个属性,接受boolean类型值,配置Lint检查出的错误报告是否应该包含解释说明,默认开启
9. htmlOutput
是一个属性,接受一个File类型参数,配置HTML报告输出的文件路径
android {
lintOptions {
htmlOutput new File("${buildDir}/lintReports/lint-results.html")
}
}
10. htmlReport
是一个属性,接受boolean类型值,用于配置是否生成HTML报告,默认true
11. ignoreWarnings
是一个属性,接受boolean类型值,用于配置Lint是否忽略警告级别的检查,只检查错误级别的。默认false,不忽略警告级别的检查
12. lintConfig
是一个属性,接受一个File类型参数,用于指定Lint的配置文件,这是一个XML格式的文件,可以指定一些默认的设置。
13. noLines
是一个属性,接受boolean类型值,如果true,error输出将不会包含源代码的行号,默认是false
14. quiet
是一个属性,接受boolean类型值,表示是否开启安静模式,true代表安静模式,Lint分析的进度或者其他信息将不会显示。默认false
15. severityOverrides
是一个只读属性,返回一个Map类型的结果,用来获取issue的优先级。Map的key是issue id, value是优先级,优先级是"fatal"、"error"、"warning"、"informational"、"ignore"
16. showAll
是一个属性,接受boolean类型值,用于标记是否应该显示所有的输出,不会对过长的消息截断等
17. textOutput
是一个只读属性,也有对应同名方法,接受一个File类型参数,用于指定生成的text格式的报告路径。如果指定stdout这个值,会被指向标准的输出,一般是终端控制台
18. textReport
是一个属性,接受boolean类型值,用于配置是否生成text报告,默认false,不生成报告
19. warningsAsErrors
是一个属性,接受boolean类型值,用于配置是否把所有的警告也当做错误处理,默认false。
20. xmlOutput
是一个属性,接受一个File类型参数,用于生成XML报告的路径
21. xmlReport
是一个属性,接受boolean类型值,用于控制是否生成XML格式的报告,默认true
22. error、fatal、ignore、warning、informational
这5个方法用来配置issue的优先级,接受的都是issue id作为参数。
/** A severity for Lint. Corresponds to com.android.tools.lint.detector.api.Severity#FATAL */
int SEVERITY_FATAL = 1;
/** A severity for Lint. Corresponds to com.android.tools.lint.detector.api.Severity#ERROR */
int SEVERITY_ERROR = 2;
/** A severity for Lint. Corresponds to com.android.tools.lint.detector.api.Severity#WARNING */
int SEVERITY_WARNING = 3;
/** A severity for Lint. Corresponds to com.android.tools.lint.detector.api.Severity#INFORMATIONAL */
int SEVERITY_INFORMATIONAL = 4;
/** A severity for Lint. Corresponds to com.android.tools.lint.detector.api.Severity#IGNORE */
int SEVERITY_IGNORE = 5;
/**
* A severity for lint. This severity means that the severity should be whatever the default
* is for this issue (this is used when the DSL just says "enable", and Gradle doesn't know
* what the default severity is.)
*/
int SEVERITY_DEFAULT_ENABLED = 6;
如果我的文章对您有帮助,不妨点个赞鼓励一下(^_^)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。