成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Android自定義Lint實(shí)踐(二)

luffyZh / 723人閱讀

摘要:自定義方案提供了一種思路將放到一個(gè)中。這樣我們就可以針對工程進(jìn)行自定義只對當(dāng)前工程有效。開發(fā)插件后,繼承了原生和自定義的所有檢查規(guī)則,內(nèi)置。我們創(chuàng)建一個(gè)內(nèi)部類來表示檢查樹的過程。

為什么需要自定義

原生Lint無法滿足我們團(tuán)隊(duì)特有的需求,例如:編碼規(guī)范。

原生Lint存在一些檢測缺陷或者缺少一些我們認(rèn)為有必要的檢測。

自定義方案

LinkedIn提供了一種思路 : 將jar放到一個(gè)aar中。這樣我們就可以針對工程進(jìn)行自定義Lint,lint.jar只對當(dāng)前工程有效。

Google指出,aar文件可以包含一個(gè)自定義的lint.jar文件

aar雖然方便,但依然有很多問題,原因在于,要想統(tǒng)一開發(fā)者的lint檢查,每個(gè)開發(fā)者都需要配置lint.xml、lintOptions。

開發(fā)插件,統(tǒng)一管理lint.xml和lintOptions,自動(dòng)添加aar。

開發(fā)插件后,繼承了原生lint和自定義lint的所有檢查規(guī)則,內(nèi)置lintOptions。

方案實(shí)施 創(chuàng)建Android項(xiàng)目

該項(xiàng)目主要用于測試規(guī)則是否正確

創(chuàng)建lint依賴項(xiàng)目

該項(xiàng)目主要用于將lint.jar轉(zhuǎn)換為lint.aar文件,并提供maven庫發(fā)布功能

創(chuàng)建lint的Java項(xiàng)目

該項(xiàng)目主要用于編寫lint的自定義規(guī)則類,以及打包成lint.jar文件

創(chuàng)建plugin的groovy項(xiàng)目

該項(xiàng)目主要用于編寫lint的引用,lint的自定義規(guī)則,并提供maven庫發(fā)布功能

自定義lint類

引入lint-api和lint-checks的依賴包

創(chuàng)建IssueRegistry類,用于注冊所有的ISSUE

創(chuàng)建相關(guān)的Detector類,繼承自Detector,實(shí)現(xiàn)相關(guān)的接口

以CustomEquaslDetector類為例,其繼承Detector類,實(shí)現(xiàn)JavaScanner接口。注意Detector類是一個(gè)抽象類,其包含了很多內(nèi)部接口類,并實(shí)現(xiàn)了它們的所有方法。接口類如下所示:

XmlScanner

ResourceFolderScanner

OtherFileScanner

JavaScanner

GradleScanner

ClassScanner

BinaryResoucrceScanner

看類的名稱就知道其相對應(yīng)的作用,所以我們實(shí)現(xiàn)JavaScanner類。

創(chuàng)建ISSUE對象,并注冊到IssueRegistry類中,創(chuàng)建對象的方式是靜態(tài)工程方法創(chuàng)建:

public static final Issue ISSUE = Issue.create(
        "LogUse",
        "避免使用Log/System.out.println",
        "使用Ln,防止在正式包打印log",
        Category.SECURITY, 5, Severity.ERROR,
        new Implementation(LogDetector.class, Scope.JAVA_FILE_SCOPE));

id : 唯一值,應(yīng)該能簡短描述當(dāng)前問題。利用Java注解或者XML屬性進(jìn)行屏蔽時(shí),使用的就是這個(gè)id。

summary : 簡短的總結(jié),通常5-6個(gè)字符,描述問題而不是修復(fù)措施。

explanation : 完整的問題解釋和修復(fù)建議。

category : 問題類別。詳見下文詳述部分。

priority : 優(yōu)先級。1-10的數(shù)字,10為最重要/最嚴(yán)重。

severity : 嚴(yán)重級別:Fatal, Error, Warning, Informational, Ignore。

Implementation : 為Issue和Detector提供映射關(guān)系,Detector就是當(dāng)前Detector。聲明掃描檢測的范圍Scope,Scope用來描述Detector需要分析時(shí)需要考慮的文件集,包括:Resource文件或目錄、Java文件、Class文件。

相對應(yīng)的,其在lint的html報(bào)告中對應(yīng)的關(guān)系,如下:

總結(jié)下,每個(gè)Lint檢查都需要四部分:

Issues 一個(gè)issue對應(yīng)于Android項(xiàng)目中的一個(gè)可能的問題或bug。

Detectors 一個(gè)detector用于搜尋代碼潛在的Issues,一個(gè)多帶帶的detector可以搜尋多個(gè)獨(dú)立但相關(guān)的Issues。

implementations 一個(gè)implementation將一個(gè)Issue連接到對應(yīng)的Detector類,并指定在哪兒搜尋Issue。

Registries 一個(gè)注冊類包含一系列的Issues,默認(rèn)的Registry類是BuiltinIssueRegistry類,因?yàn)槲覀兙帉懥俗约旱淖远xIssues,所以我們需要提供自定義Registry類。

舉個(gè)例子:

public class EnumDetector extends Detector implements Detector.JavaScanner {

    ... // Implementation and Issue code from above

    /**
     * Constructs a new {@link EnumDetector} check
     */
    public EnumDetector() {
    }

    @Override
    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
        return true;
    }

    @Override
    public EnumSet getApplicableFiles() {
        return Scope.JAVA_FILE_SCOPE;
    }

    @Override
    public List> getApplicableNodeTypes() {
        return Arrays.>asList(
                EnumDeclaration.class
        );
    }

    @Override
    public AstVisitor createJavaVisitor(@NonNull JavaContext context) {
        return new EnumChecker(context);
    }

    private static class EnumChecker extends ForwardingAstVisitor {

        private final JavaContext mContext;

        public EnumChecker(JavaContext context) {
            mContext = context;
        }

        @Override
        public boolean visitEnumDeclaration(EnumDeclaration node) {
            mContext.report(ISSUE, Location.create(mContext.file),                ISSUE.getBriefDescription(TextFormat.TEXT));
            return super.visitEnumDeclaration(node);
        }
    }
}

appliesTo方法 決定是否給定的文件可用并可被掃描,我們r(jià)eturn true來檢查給定的范圍

getApplicableFiles方法定義了Detector的范圍,該例是所有的Java文件。

getApplicableNodeTypes方法,注意其中的node,指的是一段代碼。一個(gè)node可以是一個(gè)類的申明,一個(gè)方法的調(diào)用,或者一個(gè)注釋,因?yàn)槲覀冎魂P(guān)心Enum的申明,所有返回 EnumDeclaration.class。

createJavaVisitor方法是Lombok遍歷Java樹的方法。我們創(chuàng)建一個(gè)EnumChecker內(nèi)部類來表示檢查node樹的過程。

因?yàn)橹挥幸粋€(gè)node類型需要被檢查,所有覆寫visitEnumDeclaration方法。每當(dāng)有一個(gè)Enum的申明,該方法就會被執(zhí)行一次。

mContext.report方法用于問題的報(bào)告。ISSUE為哪一種Issue,location為問題的發(fā)現(xiàn)地,以及Issue的簡要描述。

在看下IntentExtraKeyDetector類:

public class IntentExtraKeyDetector extends Detector implements JavaScanner {
    public static final Issue ISSUE = Issue.create(
            "extraKey",
            "please avoid use hardcode defined intent extra key",
            "defined in another activity",
            Category.SECURITY, 5, Severity.ERROR,
            new Implementation(IntentExtraKeyDetector.class,             Scope.JAVA_FILE_SCOPE));

    public IntentExtraKeyDetector() {}

    @Override
    public boolean appliesTo(@NonNull Context context, @NonNull File file) {
        return true;
    }

    @NonNull
    @Override
    public Speed getSpeed() {
        return Speed.FAST;
    }

    // ---- Implements JavaScanner ----

    @Override
    public List getApplicableMethodNames() {
        return Collections.singletonList("putExtra");
    }

    @Override
    public void visitMethod(@NonNull JavaContext context, @Nullable AstVisitor visitor,
                            @NonNull MethodInvocation node) {
        ResolvedNode resolved = context.resolve(node);
        if (resolved instanceof ResolvedMethod) {
            ResolvedMethod method = (ResolvedMethod) resolved;

            if (method.getContainingClass().isSubclassOf("android.content.Intent", false)
                    && method.getArgumentCount() == 2) {
                ensureExtraKey(context, node);
            }
        }
    }

    private static void ensureExtraKey(JavaContext context, @NonNull MethodInvocation node) {
        //獲取method的參數(shù)值
        StrictListAccessor accessor = node.astArguments();

        if (accessor.size() != 2) {
            return;
        }
        Expression expression = accessor.first();
        //當(dāng)?shù)谝粋€(gè)參數(shù)值類型為String,這樣是硬編碼
        if (expression instanceof StringLiteral){
            context.report(ISSUE, node, context.getLocation(node), "please avoid use hardcode defined Intent.putExtra key");
            return;
        }
        //當(dāng)?shù)谝粋€(gè)參數(shù)值類型為變量
        //ConstantEvaluator.evaluate(context, expression);
        if (expression instanceof VariableReference){
            //獲取該變量的定義name
            String targetName = ((VariableReference)expression).astIdentifier().astValue();
            if (!targetName.startsWith("EXTRA_")){
                context.report(ISSUE, node, context.getLocation(node), "please defined intent extra key start with EXTRA_");
            }
        }
        //當(dāng)?shù)谝粋€(gè)參數(shù)值是其他類的變量時(shí)
        if (expression instanceof Select){
            String targetName = ((Select)expression).astIdentifier().astValue();
            if (!targetName.startsWith("EXTRA_")){
                context.report(ISSUE, node, context.getLocation(node), "please defined intent extra key start with EXTRA_");
            }
        }
    }
使用指南

在project中的build.gradle文件中的dependencies中添加

    classpath "com.mucfc.muna.lint:plugin:latest.integration"

在module app中的build.gradle文件中,添加:

    apply plugin: "MuLintPlugin"        
融合項(xiàng)目后 使用系統(tǒng)Toast,而沒有使用muna中的自定義toast

使用Bundle.putXXX("key","value")

注意key不應(yīng)該這樣定義

使用Intent.putExtra(key,value);

注意key不能直接硬編碼,且key定義的String引用必須為EXTRA_開頭

定義Activity類必須繼承BaseActivity(Fragment類同)

注意因?yàn)樽约壕帉懙腂aseActivity,可以使用@Suppressint("activityUse")去除錯(cuò)誤

使用equals方法

在equals(value)中,value不能為硬編碼或定義在該類中的static final字符串,因?yàn)楫?dāng)為指定字符串的時(shí)候,需要value.equals(),防止空指針

使用原始的Log.d()方法

因?yàn)橛蠱uLog,所以不應(yīng)該再次使用Log.d,防止敏感信息泄露。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/65055.html

相關(guān)文章

  • 大廠黑科技 - 收藏集 - 掘金

    摘要:模仿的功能掘金本模仿了的功能。國內(nèi)曾經(jīng)出現(xiàn)的團(tuán)購類網(wǎng)站有多家,到四年多以后的現(xiàn)在,美團(tuán)已經(jīng)是成為國內(nèi)最大的本地生活服務(wù)平臺,不管怎餓了么移動(dòng)的架構(gòu)演進(jìn)掘金引言時(shí)代演進(jìn),技術(shù)也隨之發(fā)展。 模仿 Smartisan OS 的 BigBang 功能 ??? - Android - 掘金 本 Demo 模仿了 Smartisan OS 的 BigBang 功能。App 打開會從剪切板讀取文字并...

    fanux 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<