Bài viết này tôi sẽ giới thiệu về phương pháp mở rộng lint và tạo custom rule.
Lint và cách custom rule của lint như thế nào?
Lint là một bộ phân tích tĩnh, có mục tiêu tìm lỗi trên mã nguồn của bạn mà không cần phải biên dịch hoặc chạy nó.
Để tạo custom rule của lint thì cần phải tạo Detector, Issue, Registry.
Creating your rules module
Chúng ta bắt đầu bằng cách định nghĩa một module Java / Kotlin riêng biệt. Sau đó chúng ta sẽ thêm vào build.gradle như sau:
apply plugin: 'java-library'
apply plugin: 'kotlin'
dependencies {
compileOnly "com.android.tools.lint:lint-api:26.5.3"
compileOnly "com.android.tools.lint:lint-checks:26.5.3"
}
jar {
manifest {
attributes("Lint-Registry-v2": "techover.rules.IssueRegistry")
}
}
Creating Detector
Detector là một lớp có thể tìm thấy một hay nhiều vấn đề cụ thể hơn. Tùy thuộc vào vấn đề, chúng ta có thể sử dụng các loại phát hiện khác nhau:
SourceCodeScanner – một trình phát hiện chuyên về các tệp nguồn Java / Kotlin.
XmlScanner – một trình phát hiện chuyên về các tệp XML.
GradleScanner – một trình phát hiện chuyên về các tệp Gradle.
ResourceFolderScanner – một trình phát hiện chuyên về các thư mục tài nguyên (không phải các tệp mà nó chứa).
Bạn có thể tham khảo tạo Detector dưới đây:
package techover.rules
import com.android.tools.lint.detector.api.*
import com.intellij.psi.PsiMethod
import org.jetbrains.uast.UCallExpression
/**
* This detector will report any usages of the android.util.Log.
*/
class AndroidLogDetector : Detector(), SourceCodeScanner {
override fun getApplicableMethodNames(): List<String> =
listOf("tag", "format", "v", "d", "i", "w", "e", "wtf")
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
super.visitMethodCall(context, node, method)
val evaluator = context.evaluator
if (evaluator.isMemberInClass(method, "android.util.Log")) {
reportUsage(context, node)
}
}
private fun reportUsage(context: JavaContext, node: UCallExpression) {
context.report(
issue = ISSUE,
scope = node,
location = context.getCallLocation(
call = node,
includeReceiver = true,
includeArguments = true
),
message = "android.util.Log usage is forbidden."
)
}
companion object {
private val IMPLEMENTATION = Implementation(
AndroidLogDetector::class.java,
Scope.JAVA_FILE_SCOPE
)
val ISSUE: Issue = Issue
.create(
id = "AndroidLogDetector",
briefDescription = "The android Log should not be used",
explanation = """
For amazing showcasing purposes we should not use the Android Log. We should the
AmazingLog instead.
""".trimIndent(),
category = Category.CORRECTNESS,
priority = 9,
severity = Severity.ERROR,
androidSpecific = true,
implementation = IMPLEMENTATION
)
}
}
- Nó extends Detector để Android Lint có thể sử dụng để phát hiện sự cố.
- Nó extends SourceCodeScanner vì chúng ta cần kiểm tra cả hai tệp Kotlin và Java.
- getApplossibleMethodNames – chỉ lọc các chữ ký phương thức tồn tại trong android.util.Log
- visitMethodCall – sử dụng trình đánh giá để đảm bảo rằng phương thức này được gọi bởi android.util.Log chứ không phải bởi bất kỳ lớp nào khác. Ví dụ: AmazingLog có cùng phương thức và không nên gắn cờ.
- reportUsage – được sử dụng để báo cáo sự cố khi tìm thấy.
Creating Issue
Issue là một lỗi tiềm ẩn trong ứng dụng Android. Đây là cách bạn khai báo lỗi mà quy tắc của bạn sẽ giải quyết.
id – để xác định duy nhất vấn đề này.
briefDescription– mô tả tóm tắt về vấn đề.
explanation – nên là một mô tả sâu hơn về vấn đề và lý tưởng về cách giải quyết vấn đề.
category – xác định loại vấn đề. Có rất nhiều danh mục có thể có như CORRECTNESS, USABILITY, I18N, COMPLIANCE, PERFORMANCE, …
priority – một số từ 1 đến 10, trong đó số càng lớn thì vấn đề càng nghiêm trọng.
severity – nó có thể là một trong những giá trị sau: FATAL, ERROR, WARNING, INFORMATIONAL and IGNORE. Lưu ý: Nếu mức độ nghiêm trọng là FATAL hoặc ERROR thì việc chạy lint sẽ thất bại và bạn sẽ phải giải quyết vấn đề.
implementation – lớp chịu trách nhiệm phân tích tệp và phát hiện vấn đề.
Bạn có thể tham khảo tạo Issue dưới đây:
val ISSUE: Issue = Issue
.create(
id = "AndroidLogDetector",
briefDescription = "The android Log should not be used",
explanation = """
For amazing showcasing purposes we should not use the Android Log. We should the
AmazingLog instead.
""".trimIndent(),
category = Category.CORRECTNESS,
priority = 9,
severity = Severity.ERROR,
androidSpecific = true,
implementation = IMPLEMENTATION
)
Creating Registry
Bạn có thể tham khảo Registry dưới đây:
package techover.rules
import com.android.tools.lint.client.api.IssueRegistry
import com.android.tools.lint.detector.api.CURRENT_API
import com.android.tools.lint.detector.api.Issue
class IssueRegistry : IssueRegistry() {
override val api: Int = CURRENT_API
override val issues: List<Issue>
get() = listOf(AndroidLogDetector.ISSUE)
}
Run Lint
Chúng ta sẽ thêm vào app/build.gradle như sau:
dependencies {
lintChecks project(path: ':rules')
}
Sau đó chúng ta sẽ chạy command dưới đây:
./gradlew app:lintDebug
Nếu source không có Issue mà chúng ta đã Registry thì sẽ in ra nội dung như sau:
> Task :app:lintDebug
Wrote HTML report to ~/Techover/Lint/app/build/reports/lint-results-debug.html
Wrote XML report to ~/Techover/Lint/app/build/reports/lint-results-debug.xml
BUILD SUCCESSFUL
Ví dụ, trong class MainActivity bạn có log như sau:
Log.d("MainActivity", "https://magz.techover.io/")
Thì chúng ta thấy source có Issue mà chúng ta đã Registry nê sẽ in ra nội dung như sau:
> Task :app:lintDebug FAILED
Wrote HTML report to ~/Techover/Lint/app/build/reports/lint-results-debug.html
Wrote XML report to ~/Techover/Lint/app/build/reports/lint-results-debug.xml
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':app:lintDebug'.
> Lint found errors in the project; aborting build.
Fix the issues identified by lint, or add the following to your build script to proceed with errors:
...
android {
lintOptions {
abortOnError false
}
}
...
Errors found:
~/Techover/Lint/app/src/main/java/techover/lint/MainActivity.kt:13: Error: android.util.Log usage is forbidden. [AndroidLogDetector]
Log.d("MainActivity", "https://magz.techover.io/")
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED
Link tham khảo:
- https://github.com/fabiocarballo/lint-sample