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 |
댓글