In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-01 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/01 Report--
Kotlin code inspection example analysis, many novices are not very clear about this, in order to help you solve this problem, the following editor will explain for you in detail, people with this need can come to learn, I hope you can gain something.
Background
Kotlin has many features, such as null pointer security, method extension, support for functional programming, rich syntax sugar, and so on. These features make the code of Kotlin much more concise and elegant than Java, improve the readability and maintainability of the code, save development time and improve development efficiency. This is also why our team turned to Kotlin, but in the actual use, we found that seemingly simple Kotlin code may hide additional overhead that can not be ignored. The following section examines the hidden overhead of Kotlin and explores and practices how to avoid it.
Hidden overhead of Kotlin
Associated object
Accompanying objects are created by using companion object in a class to replace static members, similar to static inner classes in Java. So it is common to declare constants in concomitant objects, but if you write them incorrectly, it may incur extra overhead. For example, the following code declares the Version constant:
Call the static method of the accompanying object
Call the instance method of the accompanying object
Call the static method of the main class
Read static fields in the main class
Such Kotlin code is undoubtedly inefficient at the expense of invoking four methods in order to access a constant.
We can reduce the generated bytecode through the following solutions:
For primitive types and strings, you can use the const keyword to declare constants as compile-time constants.
For public fields, you can use the @ JvmField annotation.
For other types of constants, it is best to store common global constants in their own main class objects rather than accompanying objects.
Lazy () delegate attribute
The lazy () delegate property can be used for lazy loading of read-only properties, but an optional model parameter is often overlooked when using lazy ():
LazyThreadSafetyMode.SYNCHRONIZED: there is a double lock check when initializing the property, ensuring that the value is calculated in only one thread and that all threads get the same value.
LazyThreadSafetyMode.PUBLICATION: multiple threads execute at the same time, and the function that initializes the property is called multiple times, but only the first returned value is treated as the value of the delegate property.
LazyThreadSafetyMode.NONE: there is no double lock checking and should not be used in multithreading.
Lazy () specifies LazyThreadSafetyMode.SYNCHRONIZED by default, which may cause unnecessary thread-safety overhead, so you should specify an appropriate model to avoid unwanted synchronization locks based on the actual situation.
Basic type array
There are three array types in Kotlin:
IntArray,FloatArray, other: array of primitive types, compiled to int [], float [], others
Array: array of non-empty objects
Array: array of nullable objects
Using these three types to declare arrays, you can see the difference between them:
Array declared by Kotlin
Equivalent Java code:
An array equivalent to the declaration of Java
The latter two methods boxed the basic types, resulting in additional overhead.
So when you need to declare a non-empty array of primitive types, you should use xxxArray to avoid automatic boxing.
For cycle
Kotlin provides downTo, step, until, reversed and other functions to help developers use the for loop more easily. If using these functions alone is really convenient, simple and efficient, but what if you combine the two of them? Such as the following:
The above for loop uses a combination of downTo and step, so how is the equivalent Java code implemented?
Focus on this line of code:
IntProgression var10000 = RangesKt.step (RangesKt.downTo (10,1), 2)
This line of code creates two IntProgression temporary objects, adding extra overhead.
Exploration of Kotlin checking tool
Kotlin has more than a few hidden overhead listed above. To avoid the overhead, we need to implement a tool that checks Kotlin syntax, lists irregular code, and gives suggestions for modification. At the same time, in order to ensure that the developer's code is checked by tools, the whole inspection process should be automated.
Furthermore, the inspection rules of Kotlin code should be extensible to facilitate other users to customize their own inspection rules.
Based on this, the whole tool mainly includes the following three aspects:
Parsing Kotlin code
Write extensible custom code check rules
Inspection automation
Combined with the demand for tools, after thinking and consulting data, three options are identified:
Ktlint
Ktlint is a tool for checking the style of Kotlin code, which is different from our tool positioning and requires a lot of modification.
Detekt
Detekt is a tool for static analysis of Kotlin code, which meets our needs, but it is not suitable for Android projects, such as not being able to specify variant (variant) checks. In addition, in the whole inspection process, a kt file can only be checked once, and the check results (at that time) only support console output, which is not easy to read.
Revamping Lint
Transform Lint to increase Lint's support for Kotlin code inspection, on the one hand, the functions provided by Lint can fully meet our needs, but also support the inspection of resource files and class files, on the other hand, the reformed Lint and Lint are very similar, and the cost of learning is low.
Compared with the first two schemes, scenario 3 has the highest cost-benefit ratio, so we decided to transform Lint into a Kotlin Lint (KLint) plug-in.
Let's get an overview of the workflow of Lint, as shown in the following figure:
Lint flow chart
Obviously, the red box in the image above needs to be modified to fit the Kotlin. The main work is as follows:
Create a KotlinParser object to parse the Kotlin code
Get the jar package of custom KLint rules from aar
The Detector class needs to define a new set of interface methods to adapt to the call when traversing the Kotlin node callback
Kotlin code parsing
Like Java, Kotlin has its own abstract syntax tree. Unfortunately, there is no single library for parsing the Kotlin syntax tree, which can only be parsed through the related classes in the Kotlin compiler library. KLint uses the kotlin-compiler-embeddable:1.1.2-5 library.
Public KtFile parseKotlinToPsi (@ NonNull File file) {try {org.jetbrains.kotlin.com.intellij.openapi.project.Project ktProject = KotlinCoreEnvironment.Companion.createForProduction (()-> {}, new CompilerConfiguration (), CollectionsKt.emptyList ()) .getProject (); this.psiFileFactory = PsiFileFactory.getInstance (ktProject); return (KtFile) psiFileFactory.createFileFromText (file.getName (), KotlinLanguage.INSTANCE, readFileToString (file, "UTF-8")) } catch (IOException e) {e.printStackTrace ();} return null;} / / can be ignored, just convert the file into a character stream public static String readFileToString (File file, String encoding) throws IOException {FileInputStream stream = new FileInputStream (file); String result = null; try {result = readInputStreamToString (stream, encoding) } finally {try {stream.close ();} catch (IOException e) {/ / ignore}} return result;}
The above code can be encapsulated into a KotlinParser class, and its main function is to convert .Kt files into KtFile objects.
After calling KtFile.acceptChildren (KtVisitorVoid) when checking the Kotlin file, KtVisitorVoid will call back the method of traversing each node (Node) several times:
KtVisitorVoid visitorVoid = new KtVisitorVoid () {@ Override public void visitClass (@ NotNull KtClass klass) {super.visitClass (klass);} @ Override public void visitPrimaryConstructor (@ NotNull KtPrimaryConstructor constructor) {super.visitPrimaryConstructor (constructor);} @ Override public void visitProperty (@ NotNull KtProperty property) {super.visitProperty (property);}.}; ktPsiFile.acceptChildren (visitorVoid)
Implementation of self-defined KLint rules
The implementation of custom KLint rules refers to the Android Custom Lint practice article.
The figure above shows the files that are allowed to be included in aar, and aar can contain lint.jar, which is also implemented in this article on Android's custom Lint practice. But klint.jar cannot be put directly into aar, much less should klint.jar be renamed to lint.jar to achieve its purpose.
The final plan adopted is:
Put the klint.jar into the assets by creating the empty aar of klintrules
Modify KLint code to read klint.jar from assets
Use debugCompile when the project relies on klintrulesaar to avoid bringing klint.jar to the release package.
Definition of interface methods in Detector class
Since it's a review of the Kotlin code, it's natural for the Detector class to define a new set of interface methods. Let's first take a look at the methods provided by the Java code review rules:
I believe that students who have written Lint rules should be very familiar with the above methods. To minimize the learning cost of writing KLint inspection rules, we refer to the JavaPsiScanner interface and define a set of very similar interface methods:
The realization of KLint
Through the transformation of the above three main aspects, the KLint plug-in is completed.
Because of the similarity between KLint and Lint, the KLint plug-in is simple and easy to use:
Writing specifications similar to Lint (see the code in the last section)
Notes that support Lint support such as @ SuppressWarnings (").
The klintOptions with the same function as the Options of Lint is as follows:
MtKlint {klintOptions {abortOnError false htmlReport true htmlOutput new File (project.getBuildDir (), "mtKLint.html")}}
Inspection automation
There are two options for automatic inspection:
When developing the classmate commit/push code, trigger the pre-commit/push-hook to check, and the check does not allow commit/push.
When the pull request is created, the CI build is triggered to check that the merge is not allowed.
Here we prefer scenario 2, because pre-commit/push-hook can be bypassed by the-- no-verify command, and we want all Kotlin code to pass the check.
The KLint plug-in itself supports running through the. / gradlew mtKLint command, but considering that almost all projects perform Lint checks on CI builds, binding KLint and Lint together saves the cost of CI build scripts accessing the KLint plug-in.
Make lint task dependent on klint task with the following code to perform a KLint check before executing Lint:
/ / create KLint task and set KLint klintTask = project.getTasks () .create (String.format (TASK_NAME, "), KLint.class, new KLint.GlobalConfigAction (globalScope, null, KLintOptions.create (project)) Set lintTasks = project.tasks.findAll {it.name.toLowerCase (). Equals (" lint ")} lintTasks.each {lint-> klintTask.dependsOn lint.taskDependencies.getDependencies (lint) lint.dependsOn klintTask} / / create Klint variants task And set the for (Variant variant: androidProject.variants) {klintTask = project.getTasks () .create (String.format (TASK_NAME, variant.name.capitalize ()), KLint.class, new KLint.GlobalConfigAction (globalScope, variant) dependent by the Lint variety. KLintOptions.create (project)) lintTasks = project.tasks.findAll {it.name.startsWith ("lint") & & it.name.toLowerCase () .endsWith (variant.name.toLowerCase ())} lintTasks.each {lint-> klintTask.dependsOn lint.taskDependencies.getDependencies (lint) lint.dependsOn klintTask}}
Check real-time
Although the automation of inspection has been achieved, it can be found that the time to perform automatic inspection lags behind, often when developers prepare to combine the code, and then it is costly and risky to modify the code. Automatic checking on CI should be the last hurdle to see if there is a "fish out of the net", and the problem should be exposed during coding. Based on this, we have developed an IDE plug-in for real-time checking of Kotlin code.
KLint IDE plug-in
Through this tool, we can report errors in real time in the window of Android Studio, and help developers find problems and solve them in time.
Practice of Kotlin Code Review
The KLint plug-in is divided into two parts: the Gradle plug-in and the IDE plug-in, the former introduced in build.gradle and the latter used through Android Studio installation.
The compilation of KLint rules
The corresponding inspection rules are implemented for the case,KLint with no mode specified in lazy () listed above:
Public class LazyDetector extends Detector implements Detector.KtPsiScanner {public static final Issue ISSUE = Issue.create ("Lazy Warning", "Missing specify `lazy` mode", "see detail: https://wiki.sankuai.com/pages/viewpage.action?pageId=1322215247", Category.CORRECTNESS, 6, Severity.ERROR, new Implementation (LazyDetector.class)" EnumSet.of (Scope.KOTLIN_FILE) @ Override public List
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.