이 글은 "AndroIdiots Podcast E18: Custom Lints with Hitanshu Dhawan"(https://medium.com/androidiots/androidiots-podcast-e18-custom-lints-349f0651d458) 을 읽고 그대로 따라해 보면서 몇 가지 보충설명을 담아보았습니다. 이 글의 내용은 이 팀에서 UI 정책 같은 것들 때문에 RadioButton 을 Customize 한 IdiotRadioButton 사용하는데, 만약, RadioButton 을 그대로 사용할 경우, 경고 메시지를 주고, IdiotRadioButton 으로 수정/테스트하는 과정을 담은 것입니다.
Lint(린트) : 린트는 소스 코드를 분석하여 프로그래밍 오류, 버그, 문체 오류 및 의심스러운 구성을 표시하는 도구입니다.
Step 1. Java/Kotlin 라이브러리 모듈 생성
Step 2. Dependency 추가
:LintLibraryModule/build.gradle
dependencies {
def lintVersion = '26.5.3'
// Lint
compileOnly "com.android.tools.lint:lint-api:$lintVersion"
compileOnly "com.android.tools.lint:lint-checks:$lintVersion"
}
:app/build.gradle
dependencies {
lintChecks project(path: ':LintLibraryModule')
}
Step 3. IssueRegistry 생성
CustomCodeIssueDetector.ISSUE 는 Step 6 에서 만듭니다.
class CustomIssueRegistry : IssueRegistry() {
override val issues: List<Issue>
get() = listOf(
CustomCodeIssueDetector.ISSUE
)
}
Step 4. IssueRegistry 선언
안드로이드 스튜디오에서도 아래 그림과 같이 경고 팝업으로 알려주게 하려면 (1) gradle 코드를 추가하거나, (2) resources 에 텍스트 파일을 추가해 주어야 합니다.
1. gradle 에 추가하는 방법 : package name = com.example 일 경우,
dependencies {
def lintVersion = '26.5.3'
compileOnly "com.android.tools.lint:lint-api:$lintVersion"
...
}
jar {
manifest {
attributes("Lint-Registry-v2": "com.example.CustomIssueRegistry")
}
}
2. Resource File 추가 하는 방법 : package name = com.example 일 경우,
Step 5. Custom Detector 클래스 만들기
class CustomCodeIssueDetector : Detector()
Step 6. Custom Detector Issue 만들기
Issue 를 생성할 때 필요한 값들에 대한 설명은 굳이 하지 않아도 대략 알 수 있도록 네이밍되어 있어 생략합니다. ^^
class CustomCodeIssueDetector : Detector() {
companion object {
val ISSUE = Issue.create(
id = "IdiotRadioButtonUsageWarning",
briefDescription = "Android's RadioButton should not be used",
explanation = "Don't use Android Radio button, be an idiot and use IdiotRadioButton instead",
category = Category.CORRECTNESS,
priority = 3,
severity = Severity.WARNING,
implementation = Implementation(
CustomCodeIssueDetector::class.java,
Scope.RESOURCE_FILE_SCOPE
)
)
}
}
Step 7. Scanner 추가하기
class CustomCodeIssueDetector : Detector(), XmlScanner {
override fun getApplicableElements(): Collection<String> {
return listOf(
"RadioButton"
) // will look for Radio Button //Text in all xml
}
override fun visitElement(context: XmlContext, element: Element) {
context.report(
issue = ISSUE,
location = context.getNameLocation(element),
message = "Usage of RadioButton is prohibited" // Error message
)
}
companion object {
val ISSUE = Issue.create(...) // Step 6 참고
}
}
Step 8. Lint (Quick) Fix 추가
class CustomCodeIssueDetector : Detector(), XmlScanner {
override fun getApplicableElements(): Collection<String> { ... } // Step 7 참고
override fun visitElement(context: XmlContext, element: Element) {
val idiotRadioButtonFix = LintFix.create()
.name("Use IdiotRadioButton")
.replace()
.text("RadioButton")
.with("com.androidiots.playground.IdiotRadioButton")
.robot(true)
.independent(true)
.build()
context.report(...) // Step 7 참고
}
companion object {
val ISSUE = Issue.create(...) // Step 6 참고
}
}
여기까지가 "AndroIdiots Podcast E18: Custom Lints with Hitanshu Dhawan"(https://medium.com/androidiots/androidiots-podcast-e18-custom-lints-349f0651d458) 에서 RadioButton 을 추가할 경우, Lint 를 이용해 경고메시지를 주고, RadioButton IdiotRadioButton 으로 Quick Fix 하는 코드입니다.
테스트 코드 작성
1. 테스트를 위한 dependency 추가
:LintLibraryModule/build.gradle
dependencies {
def lintVersion = '26.5.3'
...
testImplementation "com.android.tools.lint:lint:$lintVersion"
testImplementation "com.android.tools.lint:lint-tests:$lintVersion"
testImplementation "junit:junit:4.12"
}
2. 테스트 코드 작성
:LintLibraryModule/src/test/
layout.xml 에서 RadioButton 을 사용하면 Warning 을 표시하는지 확인할 수 있는 코드
class CustomCodeIssueDetectorTest {
val testInputString = """
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
<RadioButton android:id="@+id/someidlowercase" />
</LinearLayout>
"""
@Test
fun testRadioButton() {
lint().files(
xml(
"res/layout/layout.xml", testInputString
).indented())
.issues(CustomCodeIssueDetector.ISSUE)
.allowMissingSdk(true)
.run()
.expectWarningCount(1)
}
}
Lint 관련된 여러 글들을 보면, 안드로이드 그 자체의 Lint 기능이 강력해서 이딴 기능을 굳이 쓸필요가 있느냐하는 식의 멘트들이 많이 있습니다. 이 글의 사례, RadioButton -> IdiotRadioButton 으로 사용하는 것도 그닷 좋은 방법은 아니라 ,예제만 보아도 실제 이런 Lint 가 필요하다고 보기에는 조금 설득력이 떨어집니다. 개발에 참여하는 인원이 적을 수록 별로 의미가 없는 작업이지만, 안드로이드 앱 하나에 4~50명의 개발자가 동시에 개발할 경우, 코드 컨벤션처럼 강력하게 정착시켜야 할 것은 아니지만, 개발자들의 성향(?)에 따라 느슨한 룰같은 것들이 생기기 때문에, 큰 조직이라면 어쩌면 필요할 수도 있겠다는 생각이 듭니다. "Writing a Custom Android UI Inheritance Lint Rule"(https://medium.com/@roderiklagerweij/writing-a-custom-android-ui-inheritance-lint-rule-9af254480399) 에서 나온 것들이 아마도 대표적인 사례(i.e. IncorrectViewId)가 아닐까 싶습니다. 조금 익숙해지신 분들이라면, 글보다는 github(https://github.com/roderiklagerweij/AndroidCustomLint) 코드를 보시는 것이 훨씬 빠를 것입니다.
'Android' 카테고리의 다른 글
Android targetSdkVersion=30, queryIntentActivities() 문제 (0) | 2022.01.10 |
---|---|
Gson - Android Proguard 문제 (0) | 2020.11.12 |
Android Studio - Database Inspector (0) | 2020.11.04 |
Android AlertDialog Style 변경 (0) | 2020.08.05 |
Android useClearTextTraffic, isClearTextTrafficPermitted (0) | 2020.07.15 |