http://jeremie-martinez.com/2015/12/15/custom-lint-rules
왜 custom lint 를 만들어야 하는가?
1. librady/SDK 를 개발할 때 custom lint 를 넣어주면, 사용자들이 제대로 사용하도록 가이드를 하는 역할을 한다.
2. 프로젝트 진행시 새로운 개발자에게 convention 을 자연스럽게 전달할 수 있다.
예제를 통해 배워보자.
Custom Lint 는 새로운 module 로 정의되어야만 한다.
Gradle
-
apply plugin: 'java'
targetCompatibility = JavaVersion.VERSION_1_7
sourceCompatibility = JavaVersion.VERSION_1_7
configurations {
lintChecks
}
dependencies {
compile 'com.android.tools.lint:lint-api:25.3.0'
compile 'com.android.tools.lint:lint-checks:25.3.0'
lintChecks files(jar)
}
jar {
manifest {
attributes('Lint-Registry': 'com.cklee.lint.MyCustomLintRegistry')
}
}
defaultTasks 'assemble'
task install(type: Copy, dependsOn: build) {
from configurations.lintChecks
into System.getProperty('user.home') + '/.android/lint/'
}
-
compile 하고 deploy 하려면 아래 명령어를 사용한다.
$ gradlew clean install
예제 1 : Custom Attr 는 prefix 가 있어야 한다.
-
public class AttrPrefixDetector extends ResourceXmlDetector { private static final String PREFIX = “ck”; public static final Issue ISSUE = Issue.create("AttrNotPrefixed”, // id, must be unique "You must prefix your custom attr by `” + PREFIX + “‘", // brief desc "We prefix all our attrs to avoid clashes.”, // detailed desc Category.TYPOGRAPHY, // category 5, // priority, 1~10 Severity.WARNING, // severity new Implementation(AttrPrefixDetector.class, Scope.RESOURCE_FILE_SCOPE)); // bridge b/w Detector & Scope // Only XML files @Override public boolean appliesTo(@NonNull Context context, @NonNull File file) { return LintUtils.isXmlFile(file); } // Only values folder @Override public boolean appliesTo(ResourceFolderType folderType) { return ResourceFolderType.VALUES == folderType; } // Only attr tag @Override public Collection<String> getApplicableElements() { return Collections.singletonList(TAG_ATTR); } // Only name attribute @Override public Collection<String> getApplicableAttributes() { return Collections.singletonList(ATTR_NAME); } @Override public void visitElement(XmlContext context, Element element) { final Attr attributeNode = element.getAttributeNode(ATTR_NAME); if (attributeNode != null) { final String val = attributeNode.getValue(); if (val.startsWith("android:") == false && val.startsWith(PREFIX) == false) { context.report(ISSUE, attributeNode, context.getLocation(attributeNode), "You must prefix your custom attr by `" + PREFIX + "`"); } } } }
예제 2. production 에서 Log 금지
-
public class LoggerUsageDetector 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(LoggerUsageDetector.class, Scope.CLASS_FILE_SCOPE)); @Override public List<String> getApplicableCallNames() { return Arrays.asList("v", "d", "i", "w", "e", "wtf"); } @Override public List<String> getApplicableMethodNames() { return Arrays.asList("v", "d", "i", "w", "e", "wtf"); } @Override public 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`"); } } }
등록
-
public final class MyCustomLintRegistry extends IssueRegistry { @Override public List<Issue> getIssues() { return Arrays.asList(LoggerUsageDetector.ISSUE, AttrPrefixDetector.ISSUE); } }
-
끝!
느낌은 저정도이고, 제대로 구현하려면 ref doc 을 잘 참조하자!
'프로그래밍 놀이터 > 안드로이드, Java' 카테고리의 다른 글
[android] Android SDK-tools, SDK Build-tools, Platform-tools (0) | 2020.08.02 |
---|---|
[android] Gradle 에 쫄지 말아라 (gradle 잘 이용하자) (0) | 2020.08.01 |
[android] Direct Share since MOS (0) | 2020.07.30 |
[android] RecyclerView 에서 Drag 와 Swipe 구현하기 (1) | 2020.07.29 |
[Java] Generational Concurrent GC 에 대해 알아보자 (0) | 2020.07.28 |
댓글