摘要:自定义方案提供了一种思路将放到一个中。这样我们就可以针对工程进行自定义只对当前工程有效。开发插件后,继承了原生和自定义的所有检查规则,内置。我们创建一个内部类来表示检查树的过程。
为什么需要自定义
原生Lint无法满足我们团队特有的需求,例如:编码规范。
原生Lint存在一些检测缺陷或者缺少一些我们认为有必要的检测。
自定义方案LinkedIn提供了一种思路 : 将jar放到一个aar中。这样我们就可以针对工程进行自定义Lint,lint.jar只对当前工程有效。
Google指出,aar文件可以包含一个自定义的lint.jar文件
aar虽然方便,但依然有很多问题,原因在于,要想统一开发者的lint检查,每个开发者都需要配置lint.xml、lintOptions。
开发插件,统一管理lint.xml和lintOptions,自动添加aar。
开发插件后,继承了原生lint和自定义lint的所有检查规则,内置lintOptions。
方案实施 创建Android项目该项目主要用于测试规则是否正确
创建lint依赖项目该项目主要用于将lint.jar转换为lint.aar文件,并提供maven库发布功能
创建lint的Java项目该项目主要用于编写lint的自定义规则类,以及打包成lint.jar文件
创建plugin的groovy项目该项目主要用于编写lint的引用,lint的自定义规则,并提供maven库发布功能
自定义lint类引入lint-api和lint-checks的依赖包
创建IssueRegistry类,用于注册所有的ISSUE
创建相关的Detector类,继承自Detector,实现相关的接口
以CustomEquaslDetector类为例,其继承Detector类,实现JavaScanner接口。注意Detector类是一个抽象类,其包含了很多内部接口类,并实现了它们的所有方法。接口类如下所示:
XmlScanner
ResourceFolderScanner
OtherFileScanner
JavaScanner
GradleScanner
ClassScanner
BinaryResoucrceScanner
看类的名称就知道其相对应的作用,所以我们实现JavaScanner类。
创建ISSUE对象,并注册到IssueRegistry类中,创建对象的方式是静态工程方法创建:
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 : 唯一值,应该能简短描述当前问题。利用Java注解或者XML属性进行屏蔽时,使用的就是这个id。
summary : 简短的总结,通常5-6个字符,描述问题而不是修复措施。
explanation : 完整的问题解释和修复建议。
category : 问题类别。详见下文详述部分。
priority : 优先级。1-10的数字,10为最重要/最严重。
severity : 严重级别:Fatal, Error, Warning, Informational, Ignore。
Implementation : 为Issue和Detector提供映射关系,Detector就是当前Detector。声明扫描检测的范围Scope,Scope用来描述Detector需要分析时需要考虑的文件集,包括:Resource文件或目录、Java文件、Class文件。
相对应的,其在lint的html报告中对应的关系,如下:
总结下,每个Lint检查都需要四部分:
Issues 一个issue对应于Android项目中的一个可能的问题或bug。
Detectors 一个detector用于搜寻代码潜在的Issues,一个多带带的detector可以搜寻多个独立但相关的Issues。
implementations 一个implementation将一个Issue连接到对应的Detector类,并指定在哪儿搜寻Issue。
Registries 一个注册类包含一系列的Issues,默认的Registry类是BuiltinIssueRegistry类,因为我们编写了自己的自定义Issues,所以我们需要提供自定义Registry类。
举个例子:
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 EnumSetgetApplicableFiles() { 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方法 决定是否给定的文件可用并可被扫描,我们return true来检查给定的范围
getApplicableFiles方法定义了Detector的范围,该例是所有的Java文件。
getApplicableNodeTypes方法,注意其中的node,指的是一段代码。一个node可以是一个类的申明,一个方法的调用,或者一个注释,因为我们只关心Enum的申明,所有返回 EnumDeclaration.class。
createJavaVisitor方法是Lombok遍历Java树的方法。我们创建一个EnumChecker内部类来表示检查node树的过程。
因为只有一个node类型需要被检查,所有覆写visitEnumDeclaration方法。每当有一个Enum的申明,该方法就会被执行一次。
mContext.report方法用于问题的报告。ISSUE为哪一种Issue,location为问题的发现地,以及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的参数值 StrictListAccessor accessor = node.astArguments(); if (accessor.size() != 2) { return; } Expression expression = accessor.first(); //当第一个参数值类型为String,这样是硬编码 if (expression instanceof StringLiteral){ context.report(ISSUE, node, context.getLocation(node), "please avoid use hardcode defined Intent.putExtra key"); return; } //当第一个参数值类型为变量 //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_"); } } //当第一个参数值是其他类的变量时 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"融合项目后 使用系统Toast,而没有使用muna中的自定义toast 使用Bundle.putXXX("key","value")
注意key不应该这样定义
使用Intent.putExtra(key,value);注意key不能直接硬编码,且key定义的String引用必须为EXTRA_开头
定义Activity类必须继承BaseActivity(Fragment类同)注意因为自己编写的BaseActivity,可以使用@Suppressint("activityUse")去除错误
使用equals方法在equals(value)中,value不能为硬编码或定义在该类中的static final字符串,因为当为指定字符串的时候,需要value.equals(),防止空指针
使用原始的Log.d()方法因为有MuLog,所以不应该再次使用Log.d,防止敏感信息泄露。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/65055.html
摘要:模仿的功能掘金本模仿了的功能。国内曾经出现的团购类网站有多家,到四年多以后的现在,美团已经是成为国内最大的本地生活服务平台,不管怎饿了么移动的架构演进掘金引言时代演进,技术也随之发展。 模仿 Smartisan OS 的 BigBang 功能 ??? - Android - 掘金 本 Demo 模仿了 Smartisan OS 的 BigBang 功能。App 打开会从剪切板读取文字并...
阅读 3184·2021-11-24 09:39
阅读 2922·2021-11-23 09:51
阅读 887·2021-11-18 10:07
阅读 3544·2021-10-11 10:57
阅读 2740·2021-10-08 10:04
阅读 2999·2021-09-26 10:11
阅读 1046·2021-09-23 11:21
阅读 2778·2019-08-29 17:28