博客主頁html
參考資料
https://developer.android.goo...
Android 項目通常分爲庫項目、應用項目、測試項目,對應的插件是com.android.library、com.android.application、com.android.testjava
應用項目通常只有一個,最終打包成一個APK,庫項目能夠有多個,能夠被應用項目引用。python
在Gradle中能夠建立多個項目,而且能夠經過文件夾管理,最終在settings.gradle裏配置就能夠。android
// 項目結構 MyProject + app + libraries + lib1 + lib2 settings.gradle
上面項目結構中,一個根項目MyProject,並有一個settings.gradle配置文件,Sub Project有一個應用項目 App,兩個庫項目 lib1 和 lib2 放在libraries文件夾下。git
在settings.gradle文件中配置github
// void include(String[] projectPaths); include ':app', ':libraries:lib1', ':libraries:lib1:lib2'
若是項目路徑不少,能夠下面方式指定配置shell
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版本,能夠配置修改默認發佈segmentfault
android { // 配置 發佈debug版本的aar包 defaultPublishConfig "debug" // 若是配置多個flavor,能夠配置flavor + buildtype // defaultPublishConfig "flavorDebug" }
發佈多個版本aar,默認狀況下,是不能同時發佈多個arr包,可是能夠開啓api
android { // 告訴Android Gradle插件,可同時發佈不一樣的aar包 publishNonDefault true }
其它項目就能夠引用不一樣的aar
dependencies { flavor1Implementation project(path: ':lib1', configuration: 'flavor1Release') flavor2Implementation project(path: ':lib1', configuration: 'flavor2Release') }
apply plugin: 'com.android.library' // 應用Maven倉庫 apply plugin: 'maven'
// 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會自動下載最新版本快照。
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" } } }
// 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中,有一個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的統計是已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可靈活控制每一個渠道包.
它是ProductFlavor屬性,設置該渠道的包名,想爲渠道設置特別的包名,可使用applicationId這個屬性設置
android { productFlavors { baidu { applicationId "com.gradle.test.baidu" } } }
便是一個屬性,也有一個同名的方法,只對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' } } }
啓動多個dex配置,用來突破65535方法問題。
配置混淆文件
配置簽名
是一個屬性,用來適配測試包的包名,值通常爲App的包名+.test
android { productFlavors { baidu { testApplicationId "com.gradle.test" } } }
testFunctionalTest 表示是否爲功能測試,testHandleProfiling表示是否啓動分析功能。
android { productFlavors { baidu { testFunctionalTest true testHandleProfiling true } } }
配置運行測試使用的 Instrumentation Runner 的全路徑的類名,且必須是android.app.Instrumentation 的子類。
android { productFlavors { baidu { testInstrumentationRunner "android.test.InstrumentationTestRunner" } } }
與testInstrumentationRunner一塊兒使用,配置 Instrumentation Runner 使用的參數,最終使用的都是adb shell am instrument 這個命令。testInstrumentationRunnerArguments 參數被轉換傳遞給 am instrument這個命令使用,如:-e key value
android { productFlavors { baidu { testInstrumentationRunnerArguments.put("converage", 'true') } } }
配置渠道的版本號和版本名稱
android { productFlavors { baidu { versionName "2.1.5" versionCode 215 } } }
爲了基於不一樣標準構建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對象配置:
維度優先級很重要,高優先級的flavor會替換掉低優先級的資源、代碼、配置等,上例中優先級:api>version>defaultConfig
經過dimension 指定維度後,Android Gradle會幫助生成相應 Task、SourceSet、Dependencies等。如今構建變體的產物=Api+Version+BuildType, 如:MinApi21DemoRelease、MinApi21FullRelease、MinApi21DemoDebug、MinApi21FullDebug等
生成多個渠道包主要是爲了跟蹤每一個渠道的狀況,如:新增、活躍、留存。除了根據渠道號區分每一個渠道外,大部分狀況下沒有什麼不一樣,惟一區別是屬於哪一個渠道。
由於Android Gradle對每一個渠道包都要執行構建過程,致使速度變慢。美團研究一個辦法,在APK的MEAT-INF目錄下添加空文件不用從新簽名原理。
使用時,在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爲測試程序提供了很好支持,既可使用傳統的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測試是基於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提供配置項:
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
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' } }
是一個屬性,接受boolean類型值,配置Lint發現錯誤時是否退出Gradle構建。默認true
是一個屬性,接受boolean類型值,配置錯誤的輸出裏是否應該顯示絕對路徑,默認true,顯示相對路徑
是一個屬性,也是一個方法,配置哪些項目須要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)
是一個屬性,接受boolean類型值,true表示須要檢查全部警告的issue,包括默認被關閉的issue;false不檢查
是一個屬性,接受boolean類型值,配置在release構建的過程當中,Lint 是否檢查致命的錯誤問題,默認true,一旦發現有fatal級別的問題,release構建就會終止。
用來關閉給定issue ids的Lint檢查,參數接受是issue id。
與disable相反
是一個屬性,接受boolean類型值,配置Lint檢查出的錯誤報告是否應該包含解釋說明,默認開啓
是一個屬性,接受一個File類型參數,配置HTML報告輸出的文件路徑
android { lintOptions { htmlOutput new File("${buildDir}/lintReports/lint-results.html") } }
是一個屬性,接受boolean類型值,用於配置是否生成HTML報告,默認true
是一個屬性,接受boolean類型值,用於配置Lint是否忽略警告級別的檢查,只檢查錯誤級別的。默認false,不忽略警告級別的檢查
是一個屬性,接受一個File類型參數,用於指定Lint的配置文件,這是一個XML格式的文件,能夠指定一些默認的設置。
是一個屬性,接受boolean類型值,若是true,error輸出將不會包含源代碼的行號,默認是false
是一個屬性,接受boolean類型值,表示是否開啓安靜模式,true表明安靜模式,Lint分析的進度或者其餘信息將不會顯示。默認false
是一個只讀屬性,返回一個Map類型的結果,用來獲取issue的優先級。Map的key是issue id, value是優先級,優先級是"fatal"、"error"、"warning"、"informational"、"ignore"
是一個屬性,接受boolean類型值,用於標記是否應該顯示全部的輸出,不會對過長的消息截斷等
是一個只讀屬性,也有對應同名方法,接受一個File類型參數,用於指定生成的text格式的報告路徑。若是指定stdout這個值,會被指向標準的輸出,通常是終端控制檯
是一個屬性,接受boolean類型值,用於配置是否生成text報告,默認false,不生成報告
是一個屬性,接受boolean類型值,用於配置是否把全部的警告也當作錯誤處理,默認false。
是一個屬性,接受一個File類型參數,用於生成XML報告的路徑
是一個屬性,接受boolean類型值,用於控制是否生成XML格式的報告,默認true
這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;
若是個人文章對您有幫助,不妨點個贊鼓勵一下(^_^)