自动规避代码陷阱——自定义Lint规则
生活随笔
收集整理的這篇文章主要介紹了
自动规避代码陷阱——自定义Lint规则
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
?
目錄
一、Lint是什么?
二、Lint的使用
三、為什么要使用自定義Lint規(guī)則?
四、新建module
五、在lintjar中定義規(guī)則
六、配置lintjar中gradle
七、配置LintAar的gradle
八、在項目中引入規(guī)則
九、執(zhí)行l(wèi)int
十、如何定義規(guī)則
十一、總結
?
一、Lint是什么?
Lint 是一款靜態(tài)代碼分析工具,能檢查安卓項目的源文件,從而查找潛在的程序錯誤以及優(yōu)化提升的方案。 當你忘記在Toast上調用show()時,Lint 就會提醒你。它也會確保你的ImageView中添加了contentDescription,以支持可用性。類似的例子還有成千上萬個。誠然,Lint 能在諸多方面提供幫助,包括:正確性,安全,性能,易用性,可用性,國際化等等。 這是引用網(wǎng)上的一段描述,簡單來說lint可以對代碼進行檢查分析,查找各類潛在問題。二、Lint的使用
在Android Studio中選擇Analyze -> Inspect Code 然后在彈出窗中選擇Whole project,點擊確定開始檢查 檢查結束就可以在下面看到結果了,如下圖:關于lint的使用不是本文的重點,這里只是簡單介紹一下。
三、為什么要使用自定義Lint規(guī)則?
由于項目的架構,有時候項目中會有一些非正式的代碼規(guī)則,比如不使用系統(tǒng)自帶的日志工具Log而使用第三方或二次封裝過的工具類。這種情況默認的lint就無法檢查了,這時候自定義lint規(guī)則就派上用場了。 自定義lint規(guī)則可以幫助團隊規(guī)避一些因架構、業(yè)務、歷史等原因出現(xiàn)的代碼陷阱,避免一些問題頻繁重復的產生,同時可以讓團隊新成員快速被動的了解一些開發(fā)規(guī)則。 下面開始一步步介紹如何自定義lint規(guī)則。四、新建module
想要使用自定義lint規(guī)則,一種做法是將定義規(guī)則的代碼打成jar包,然后放在“%UserHome%/.android/lint/”目錄下。 這種做法有兩個缺點:一是對所有的項目都產生作用,無法實現(xiàn)不同項目使用不同規(guī)則;二是需要每個人都下載并拷貝到目錄下。 另外一種做法將定義的規(guī)則打包成aar的形式,依賴到項目中。 這種做法需要創(chuàng)建兩個module,一個java-lib,一個android-lib,如下圖: 在lintjar中編寫規(guī)則代碼,而lintaar沒有任何代碼,它的作用是將lintjar的jar包打包成aar以便引用。五、在lintjar中定義規(guī)則
在lintjar中新建一個類,繼承Detector,實現(xiàn)一個規(guī)則,如下: public class LogDetector extends Detector implements Detector.ClassScanner {public static final Issue ISSUE = Issue.create("LogUtilsNotUsed","You must use our `LogUtils`","Logging should be avoided in production for security and performance reasons. Therefore, we created a LogUtils that wraps all our calls to Logger and disable them for release flavor.",Category.MESSAGES,9,Severity.ERROR,new Implementation(LogDetector.class,Scope.CLASS_FILE_SCOPE));@Overridepublic List<String> getApplicableCallNames() {return Arrays.asList("v", "d", "i", "w", "e", "wtf");}@Overridepublic List<String> getApplicableMethodNames() {return Arrays.asList("v", "d", "i", "w", "e", "wtf");}@Overridepublic void checkCall(@NonNull ClassContext context,@NonNull ClassNode classNode,@NonNull MethodNode method,@NonNull MethodInsnNode call) {String owner = call.owner;if (owner.startsWith("android/util/Log")) {context.report(ISSUE,method,call,context.getLocation(call),"You must use our `LogUtils`");}} } LogDetector的作用是檢查代碼中是否使用Log類,建議使用封裝過的"LogUtils"類。 其中代碼的意義和功能我們稍后再細說,目前只需要知道繼承Detector來實現(xiàn)一個規(guī)則就可以了。 然后我們還需要另外一個類,繼承IssueRegistry,這個類的作用是將定義規(guī)則注冊上,代碼很簡單,如下: public class LintRegistry extends IssueRegistry {@Overridepublic List<Issue> getIssues() {return Arrays.asList(InitCallDetector.ISSUE, LogDetector.ISSUE);} }這樣還沒有完成注冊,要完成注冊我們還需要在gradle中進行配置。?
六、配置lintjar中gradle
在lintjar的gradle中引入lint的兩個庫 dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])compile 'com.android.tools.lint:lint-api:24.3.1'compile 'com.android.tools.lint:lint-checks:24.3.1' } 然后,注冊我們之前定義好的Registry類 /***?Lint-Registry是lint的注冊類*/ jar?{manifest?{attributes("Lint-Registry":?"com.bennu.lintjar.LintRegistry")} } 最后定義一個打包方法,這個會在lintaar中使用 //定義lintJarOutput方法,在lintaar中被調用 configurations {lintJarOutput }dependencies {//lintJarOutput方法,打jar包lintJarOutput files(jar) } 這樣lintJar這個module就可以了,下面開始配置lintAar這個module。七、配置LintAar的gradle
LintAar這個module中不需要寫任何代碼,它的作用是將lintJar生成的jar包再打包成aar即可。 在LintAar的gradle中添加如下: // 定義lintJarImport方法,在copyLintJar任務中被調用 configurations {lintJarImport }dependencies {// 調用lintjar的lintJarOutput方法,獲得jar包lintJarImport project(path: ':lintjar', configuration: 'lintJarOutput') }// 調用lintJarImport得到jar包,拷貝到指定目錄 task copyLintJar(type: Copy) {from (configurations.lintJarImport) {rename {String fileName ->'lint.jar'}}into 'build/intermediates/lint/' }// 當項目執(zhí)行到prepareLintJar這一步時執(zhí)行copyLintJar方法(注意:這個時機需要根據(jù)項目具體情況改變) project.afterEvaluate {def compileLintTask = project.tasks.find{ it.name == 'prepareLintJar'}compileLintTask.dependsOn(copyLintJar) } 定義一個lintJarImport方法,這個方法會調用lintJar中的lintJarOutput方法得到jar包。 新建一個copyLintJar的任務task,目的是將前面得到的jar包拷貝到指定的目錄。 最后在afterEvaluate中判斷當執(zhí)行了‘prepareLintJar’這個task時執(zhí)行copyLintJar這個任務。 注意:‘prepareLintJar’是基于我自己的環(huán)境判斷出來的,在不同的gradle版本上可能有所不同,請根據(jù)實際情況修改copyLintJar的執(zhí)行時機。 這樣lintjar和lintaar這兩個module都完成了,下一步將它們依賴進項目。八、在項目中引入規(guī)則
在項目的gradle中引入lintaar dependencies {implementation fileTree(include: ['*.jar'], dir: 'libs')implementation project(':lintaar') } 同時添加如下配置 android {...lintOptions {textReport true // 輸出lint報告textOutput 'stdout'abortOnError false // 遇到錯誤不停止} } 然后,我們在代碼中隨便寫個Log代碼,如下: @Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.e("test", "use log.e");init(); } 接下來就可以測試自定義的規(guī)則是否生效了。九、執(zhí)行l(wèi)int
點開gradle窗口,在項目(如:app)下找到lint的相關task,雙擊執(zhí)行即可,如下 執(zhí)行時就可以中Message窗口中看到相關信息,如下 可以看到,我們定義的規(guī)則已經(jīng)使用了,找到了一處使用Log類的代碼。十、如何定義規(guī)則
上面我們實現(xiàn)了規(guī)則并成功使用了,但是對于規(guī)則的定義,即Detector類一筆帶過,這個其實才是重點,下面我們以LogDetector為例詳細說說如何定義自己的規(guī)則。 (1)首先創(chuàng)建一個Issue對象,如下: public static final Issue ISSUE = Issue.create("LogUtilsNotUsed","You must use our `LogUtils`","Logging should be avoided in production for security and performance reasons. Therefore, we created a LogUtils that wraps all our calls to Logger and disable them for release flavor.",Category.MESSAGES,9,Severity.ERROR,new Implementation(LogDetector.class,Scope.CLASS_FILE_SCOPE)); Issue的create函數(shù)有七個參數(shù):- id:問題的id
- briefDescription:問題的簡單描述
- explanation:問題的解釋,即如何解決問題
- category:問題的類型,具體間Category類
- priority:問題的重要程度,從1到10,10是最重要
- severity:問題的嚴重性,有ERROR、WARNING等
- implementation:問題的實現(xiàn),Implementation類型
- issue:上面定義的ISSUE
- method:MethodNode類型
- instruction:MethodInsnNode類型
- location:問題的位置
- message:問題描述
十一、總結
本篇文章主要是講解一下如何在項目中完成一個自定義lint的引入,并且通過一個簡單的例子講解如何創(chuàng)建一個簡單的規(guī)則,并且運行查看結果。 源碼:GitHub - chzphoenix/LintRulesForAndroid總結
以上是生活随笔為你收集整理的自动规避代码陷阱——自定义Lint规则的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android中Intent/Bundl
- 下一篇: 自定义RecyclerView动画——实