头图

Two years ago, the Android Open Source Project (AOSP) application team started to use Kotlin instead of Java to refactor AOSP applications. There are two main reasons for the refactoring: one is to ensure that AOSP applications can follow Android best practices, and the other is to provide a good example of using Kotlin for application development. One of the reasons why Kotlin is so attractive is its concise syntax. In many cases, the number of code blocks written in Kotlin is less than that of Java code blocks with the same function. In addition, Kotlin, a rich expressive programming language, also has other advantages, such as:

  • Null safety: This concept can be said to be rooted in Kotlin to help avoid destructive null pointer exceptions;
  • Concurrency: Just the description of Android in 161499f1c2b5a0 Google I/O 2019 , structured concurrency allows the use of coroutines to simplify background task management;
  • Compatible with Java: Especially in this refactoring project, the compatibility of Kotlin and Java language allows us to perform Kotlin conversion file by file.

The AOSP team published an article last summer detailing AOSP desktop clock application . And this year, we converted the AOSP calendar application from Java to Kotlin. Before this conversion, the number of lines of code applied exceeded 18,000, and the code base was reduced by approximately 300 lines after the conversion. In this conversion, we followed the similar technology as the AOSP desktop clock conversion process, took full advantage of the interoperability of Kotlin and Java language, converted the code files one by one, and used independent construction in the process The goal is to replace the Java code file with the corresponding Kotlin code file. Because there are two people in the team doing this work, we created an exclude_srcs attribute for each person in the Android.bp file, so that two people can refactor and refactor at the same time while reducing code merge conflicts. Push the code. In addition, this allows us to perform incremental testing to quickly locate the files in which the error occurred.

When converting any given file, we first use the Android Studio Kotlin plugin provided in the from Java to Kotlin. Although the plug-in successfully helped us convert most of the code, we still encountered some problems, which needed to be solved manually by the developer. The parts that need to be changed manually, we will list them in the next chapters of this article.

After converting each file to Kotlin, we manually tested the UI interface of the calendar application, ran unit tests, and ran a Compatibility Test Suite (CTS) for functional verification to ensure that no more Any regression testing.

Steps after automatic conversion

As mentioned above, after using the automatic conversion tool, there are some recurring problems that need to be manually located and resolved. In the AOSP desktop clock article, some of the problems encountered and solutions are introduced in detail. Some of the problems encountered during the AOSP calendar conversion process are listed below.

Mark the parent class with the open keyword

One of the problems we encountered was the mutual call between Kotlin parent class and child class. In Kotlin, to mark a class as inheritable, you must add the open keyword in the class declaration, and do the same for methods in the parent class that are overridden by subclasses. But inheritance in Java does not need to use the open keyword. Since Kotlin and Java can call each other, this problem did not appear until most of the code files were converted to Kotlin.

For example, in the following code snippet, a class SimpleWeeksAdapter

class MonthByWeekAdapter(context: Context?, params:
    HashMap<String?, Int?>) : SimpleWeeksAdapter(context as Context, params) {//方法体}

Since the code file conversion process is performed one file at a time, even if the SimpleWeeksAdapter.kt open keyword will not appear in the class declaration, which will cause an error. Therefore, you need to manually add the open keyword so that the SimpleWeeksAdapter class can be inherited. This special class declaration looks like this:

open class SimpleWeeksAdapter(context: Context, params: HashMap<String?, Int?>?) {//方法体}

override modifier

Similarly, methods in subclasses that override the parent class must also be override modifier. In Java, this is achieved through the @Override annotation. However, although there is a corresponding annotation implementation version in Java, the override modifier is not added to the Kotlin method declaration during the automatic conversion process. The solution is to manually add the override modifier in all appropriate places.

overwrites the attributes in the parent class

During the refactoring process, we also encountered an abnormal problem of attribute overwriting. When a subclass declares a variable, and there is a non-private variable with the same name in the parent class, we need to add a override modifier. However, even if the type of the subclass variable is different from that of the parent class variable, the override modifier must still be added. In some cases, adding override still does not solve the problem, especially when the types of subclasses are completely different. In fact, if the types do not match, adding the override open keyword 061499f1c2b889 before the variables of the parent class will cause an error:

type of *property name* doesn’t match the type of the overridden var-property

This error is very confusing, because in Java, the following code can be compiled normally:

public class Parent {
    int num = 0;
}

class Child extends Parent {
    String num = "num";
}

The corresponding code in Kotlin will report the above-mentioned error:

class Parent {
    var num: Int = 0
}

class Child : Parent() {
    var num: String = "num"
}

This question is very interesting, we currently avoid this conflict by renaming the variables in the subclass. The above Java code will be converted to the problematic Kotlin code by the code converter currently provided by Android Studio. This even is reported as a bug .

import statement

In all the files we convert, automatic conversion tools tend to truncate all import statements in the Java code to the first line in the Kotlin file. This caused some crazy errors at first, and the compiler would report "unknown references" errors throughout the code. After realizing this problem, we began to manually paste the import statement in Java into the Kotlin code file and convert it separately.

exposed member variable

By default, Kotlin will automatically generate getter and setter methods for instance variables in the class. However, sometimes we want a variable to be just a simple Java member variable. This can be achieved by using the @JvmField annotation.

@JvmField is to "instruct the Kotlin compiler not to generate getter and setter methods for this property, and use it as a member variable to allow it to be publicly accessible". This annotation CalendarData class , which contains two static final variables. By val , we ensure that these variables can be accessed by other classes as member variables, thus achieving compatibility between Java and Kotlin.

objects Static method

Functions defined in Kotlin objects must @JvmStatic to allow them to be called in Java code through method names instead of instantiation. In other words, this annotation makes it have a Java-like method behavior, that is, the method can be called by the class name. According to the Kotlin document , "The compiler generates a static method for the external class of the object, and an instance method for the object itself." We Utils file . After the conversion, Java The class becomes a Kotlin object. Subsequently, all methods defined in the object must be marked with @JvmStatic, which allows the use of syntax such as Utils.method() to be called in other files. It is worth mentioning that using .INSTANCE (ie Utils.INSTANCE.method()) between the class name and the method name is also an option, but this is not in line with the common Java grammar, and all calls to Java static methods need to be changed .

performance evaluation analysis

All benchmark tests were performed on a machine with 96 cores and 176 GiB of memory. The main indicators used in the analysis of this project have reduced the number of lines of code, the file size of the target APK, the build time, and the time from startup to display of the first screen. While analyzing each of the above factors, we also collected the data of each parameter and displayed it in a table.

Reduced number of lines of code

After the complete transition from Java to Kotlin, the number of lines of code been reduced from 161499f1c2bb3d 18,004 to 17,729 . This is more than the original amount of Java code reduced by about% 1.5 . Although the amount of code reduction is not significant, for some large-scale applications, the effect of this conversion on reducing the number of lines of code may be more significant. refer to the example in the article 161499f1c2bb43 AOSP Desktop Clock

target APK size

The size of the application APK written in Kotlin is 2.7 MB , and the size of the application APK written in Java is 2.6 MB . It can be said that this difference is basically negligible. Since some additional Kotlin libraries are included, the increase in APK size is actually expected. This size increase can be optimized Proguard or R8

compile time

Kotlin and build time is a Java application (that does not contain an abnormal value) is calculated by taking the average of 10 times to build a complete time from zero, Kotlin application average build time 13 is minutes 27 seconds , and The average build time of the Java application 12 minutes and 6 seconds . According to some information (such as " Difference between Java and Kotlin " and " Kotlin and Java "), the compilation time of Kotlin is actually more time-consuming than Java, especially for starting from scratch. Build. Some analysts assert that the compilation speed of Java will be 10-15% faster, and some analysts say this data is 15-20%. Taking our example for the time it takes to build a complete build from scratch, Java’s compilation speed is 11.2% faster than Kotlin. Although this small difference is not within the above range, it may be because the AOSP calendar is a relatively Small application, there are only 43 categories. Although a complete build from scratch is slow, Kotlin still has advantages in other areas, and these advantages should be taken into consideration. For example, compared to Java, Kotlin's more concise syntax usually guarantees a smaller amount of code, which makes the Kotlin code base easier to maintain. In addition, since Kotlin is a safer and more effective programming language, we can consider the slower complete build time to be negligible.

First screen display time

We used this method to test the time it takes for the application to start to fully display the first screen. After 10 trials, we found that the average time of Kotlin application 197.7 milliseconds , and Java's 161499f1c2bcce 194.9 milliseconds . These tests were performed on the Pixel 3a XL device. It can be concluded from this test result that Java applications may have a slight advantage compared to Kotlin applications; however, since the average time is very close, this difference is almost negligible. Therefore, it can be said that the conversion of the AOSP calendar application to Kotlin did not negatively affect the initial startup time of the application.

Conclusion

It took approximately 1.5 months (6 weeks) to convert the AOSP calendar application to Kotlin, and two interns were responsible for the implementation of the project. Once we are more familiar with the code base and better at solving recurring compile-time, runtime, and syntax problems, the efficiency will definitely become higher. In general, this particular project successfully demonstrated how Kotlin affects existing Android applications, and has taken a solid step in the transition to AOSP applications.

Welcome to Click here to submit feedback to us, or share your favorite content or problems found. Your feedback is very important to us, thank you for your support!


Android开发者
404 声望2k 粉丝

Android 最新开发技术更新,包括 Kotlin、Android Studio、Jetpack 和 Android 最新系统技术特性分享。更多内容,请关注 官方 Android 开发者文档。