Custom Lint Rules

by KINH HOANG` HON
615 views

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

Leave a Comment

* By using this form you agree with the storage and handling of your data by this website.