
Wenn der Entwickler nicht vorsichtig genug ist, kann es sehr schlecht laufen. Die klassischen Entwicklerauslassungen sind beispielsweise die Verwendung einer neuen Version der API, die nicht mit dem alten Code kompatibel ist, die Ausführung von Aktionen, für die spezielle Benutzerberechtigungen erforderlich sind, und Lücken in der Lokalisierung der Anwendung. Und das sind nur einige davon.
Darüber hinaus verfügen Java und Kotlin wie jede andere Programmiersprache über eigene Konstrukte, die zu einer schlechten Leistung führen können.
Hallo Fussel
Wir verwenden ein Tool namens Lint (oder Linter), um solche Probleme zu vermeiden. Lint ist ein statisches Code-Analyse-Tool, mit dem Entwickler potenzielle Probleme erkennen können, bevor der Code kompiliert wird. Lint führt mehrere Überprüfungen des Quellcodes durch, wodurch Probleme wie nicht verwendete Variablen oder Funktionsargumente, Vereinfachung von Bedingungen, falscher Umfang, undefinierte Variablen oder Funktionen, schlecht optimierter Code usw. erkannt werden können. Wenn wir über die Android-Entwicklung sprechen, sind Hunderte von Lint-Checks sofort einsatzbereit .
Manchmal müssen wir jedoch bestimmte Probleme in unserem Code finden, die von diesen vorhandenen Prüfungen nicht abgedeckt werden.
Hallo Custom Checks Lint
Bevor wir mit dem Codieren beginnen, definieren wir unser Ziel und sehen, wie es mithilfe der Lint-API Schritt für Schritt implementiert wird. Ziel ist es, eine Prüfung zu erstellen, um einen falschen Methodenaufruf für ein Objekt zu erkennen. Mit diesem Test soll ermittelt werden, ob die Methode zum Installieren des Listeners auf der View-Komponente so ist, dass mehrere aufeinanderfolgende Klicks auf die Komponente unterbrochen werden, sodass nicht dieselbe Aktivität geöffnet oder mehrmals auf das Netzwerk zugegriffen werden kann.
Flusenbenutzerprüfungen werden als Teil des Standardmoduls Java (oder Kotlin) geschrieben. Der einfachste Weg, um loszulegen, besteht darin, ein einfaches Gradle-basiertes Projekt zu erstellen (es muss kein Android-Projekt sein).
Fügen Sie dann die Lint-Abhängigkeiten hinzu. build.gradle
Datei build.gradle
Ihres Moduls build.gradle
hinzu:
compileOnly "com.android.tools.lint:lint-api:$lintVersion" compileOnly "com.android.tools.lint:lint-checks:$lintVersion"
Jetzt gibt es einen Trick, den ich bei der Erforschung dieses Themas gelernt habe. lintVersion
sollte gradlePluginVersion + 23.0.0
. gradlePluginVersion
ist eine Variable, die auf Projektebene in der Datei build.gradle
definiert ist. Und im Moment ist die neueste stabile Version 3.3.0. Dies bedeutet, dass lintVersion
sein sollte.
Jeder Lint Check besteht aus 4 Teilen:
- Das Problem ist das Problem in unserem Code, das wir verhindern wollen. Wenn die Flusenprüfung fehlschlägt, wird dies dem Entwickler gemeldet.
- Detector ist ein Tool zur Fehlersuche, das die Lint-API bereitstellt.
- Eine Implementierung ist ein Bereich, in dem ein Problem auftreten kann (Quelldatei, XML-Datei, kompilierter Code usw.).
- Eine Registrierung ist eine benutzerdefinierte Lint-Prüfregistrierung, die mit einer vorhandenen Registrierung verwendet wird, die vordefinierte Prüfungen enthält.
Implementierung
Beginnen wir mit der Erstellung einer Implementierung für unsere benutzerdefinierte Validierung. Jede Implementierung besteht aus einer Klasse, die einen Detektor und einen Bereich implementiert.
val correctClickListenerImplementation = Implementation(CorrectClickListenerDetector::class.java, Scope.JAVA_FILE_SCOPE)
Denken Scope.JAVA_FILE_SCOPE
daran, dass Scope.JAVA_FILE_SCOPE
auch für Kotlin-Klassen funktioniert.
Das Problem
Der nächste Schritt besteht darin, diese Implementierung zu verwenden, um das Problem zu bestimmen. Jedes Problem besteht aus mehreren Teilen:
- ID ist eine eindeutige Kennung.
- Beschreibung - eine kurze (5-6 Wörter) Erklärung des Problems.
- Erläuterung Eine vollständige Erläuterung eines Problems mit einem Vorschlag zur Behebung dieses Problems.
- Kategorie - Die Kategorie des Problems (Leistung, Übersetzung, Sicherheit usw.).
- Priorität - Die Bedeutung des Problems im Bereich von 1 bis 10, wobei 10 am höchsten ist. Dies wird verwendet, um Probleme in dem Bericht zu sortieren, der beim Start von Lint erstellt wurde.
- Schweregrad - Der Schweregrad des Problems (schwerwiegend, Fehler, Warnung, Information oder Ignorieren).
- Eine Implementierung ist eine Implementierung, mit der dieses Problem erkannt wird.
val ISSUE_CLICK_LISTENER = Issue.create( id = "UnsafeClickListener", briefDescription = "Unsafe click listener", explanation = """" This check ensures you call click listener that is throttled instead of a normal one which does not prevent double clicks. """.trimIndent(), category = Category.CORRECTNESS, priority = 6, severity = Severity.WARNING, implementation = correctClickListenerImplementation )
Detektor
Die Lint-API bietet Schnittstellen für jeden Bereich, den Sie in einer Implementierung definieren können. Jede dieser Schnittstellen bietet Methoden, mit denen Sie Teile des Codes überschreiben und darauf zugreifen können, die Sie interessieren.
- UastScanner - Java- oder Kotlin-Dateien (UAST - Unified Abstract Syntax Tree (russischer einheitlicher abstrakter Syntaxbaum )).
- ClassScanner - kompilierte Dateien (Bytecode).
- BinaryResourceScanner - Binärressourcen wie Bitmaps oder
res/raw
Dateien. - ResourceFolderScanner - Ressourcenordner (keine bestimmten Dateien darin).
- XmlScanner - XML-Dateien.
- GradleScanner - Gradle-Dateien.
- OtherFileScanner - alles andere.
Darüber hinaus ist die Detector
Klasse eine Basisklasse, die leere Implementierungen aller von jeder der oben genannten Schnittstellen bereitgestellten Methoden enthält, sodass Sie nicht die vollständige Schnittstelle implementieren müssen, wenn Sie nur eine Methode benötigen.
Jetzt können wir einen Detektor implementieren, der den korrekten Methodenaufruf für das Objekt überprüft.
private const val REPORT_MESSAGE = "Use setThrottlingClickListener" class CorrectClickListenerDetector : Detector(), Detector.UastScanner { override fun getApplicableUastTypes(): List<Class<out UElement>>? { return listOf<Class<out UElement>>(UCallExpression::class.java) } /** * UAST, , * UAST . UElementHandler, * , , * , , , , .. * , * . — , * , , . */ override fun createUastHandler(context: JavaContext): UElementHandler? { return object: UElementHandler() { override fun visitCallExpression(node: UCallExpression) { if (node.methodName != null && node.methodName?.equals("setOnClickListener", ignoreCase = true) == true) { context.report(ISSUE_CLICK_LISTENER, node, context.getLocation(node), REPORT_MESSAGE, createFix()) } } } } private fun createFix(): LintFix { return fix().replace().text("setOnClickListener").with("setThrottlingClickListener").build() } }
Die Registrierung
Das Letzte, was wir tun müssen, ist, Probleme zu unserer Registrierung hinzuzufügen und Lint mitzuteilen, dass es eine spezielle Problemregistrierung gibt, die zusammen mit der Standardregistrierung verwendet werden sollte.
class MyIssueRegistry : IssueRegistry() { override val issues: List<Issue> = listOf(ISSUE_CLICK_LISTENER) }
In der build.gradle
Modulebene:
jar { manifest { attributes("Lint-Registry-v2": "co.infinum.lint.MyIssueRegistry") } }
Dabei ist co.infinum.lint
ein Paket der Klasse MyIssueRegistry
. Jetzt können Sie die jar
Task mit dem gradlew
Skript gradlew
Die Bibliothek sollte im Verzeichnis build/libs
gradlew
.
Hier ist ein weiteres Beispiel für eine Lint-Benutzerprüfung, bei der Sie sehen können, wie XML-Dateien verarbeitet werden.
Verwenden Sie
Ihr neuer Lint-Check kann im Projekt verwendet werden. Wenn diese Prüfung auf alle Projekte angewendet werden kann, können Sie sie im Ordner ~/.android/lint
(Sie können sie erstellen, wenn sie noch nicht vorhanden ist).
Darüber hinaus können Sie Ihre Prüfung in einem separaten Modul in Ihrem Projekt lintChecks
und dieses Modul wie jede andere Abhängigkeit mit der Methode lintChecks
.
Lohnt es sich?
Lint ist ein wirklich gutes Werkzeug, das jeder Entwickler verwenden sollte. Es ist sehr hilfreich, potenzielle Probleme mit Ihrem Code frühzeitig erkennen zu können. Obwohl benutzerdefinierte Prüfungen vor allem aufgrund der Komplexität der API nicht einfach zu schreiben sind, lohnen sie sich auf jeden Fall und können Ihnen in Zukunft viel Zeit und Mühe sparen.