Public number: ByteCode, committed to sharing the latest technology original articles, involving Kotlin, Jetpack, translation, system source code, LeetCode/sabre-point Offer/multi-threading/domestic and foreign big factory algorithm and so on a series of articles.
There are no fancy tricks in this article, just some common techniques used in actual development, right
The purpose of Kotlin introduced by Google is to make Android development more convenient. Since Kotlin was officially declared as the preferred language for Android development, more and more teams have been using Kotlin in their projects.
XML is notoriously time intensive, so in Android 10.0, tryInflatePrecompiled is an optimization that runs at compile time. Because the more complex the layout file, the more time it takes XmlPullParser to parse the XML, the tryInflatePrecompiled method generates compiled_view.dex based on the precompilation of the XML and then generates the corresponding View by reflecting it. This reduces the time it takes XmlPullParser to parse XML, but is currently disabled. Source code analysis please see Android resource load source code Analysis one.
For simple layouts, Kotlin is used to rewrite this part of the UI. However, the threshold is still very high. With the advent of Jetpack Compose, it aims to make building native Android apps faster and easier. Google recently released Jetpack Compose 1.0.
Kotlin’s advantages already exist in many aspects. The combination of Kotlin’s advanced functions can make code more readable and concise, but can cause some performance losses if not used properly.
- The Kotlin Technique and Its Analysis that few people know (part 2)
- Kotlin’s Technique and Analysis (1)
In these two articles, I have shared the techniques Kotlin has used in real projects, the impact on performance and memory if not used properly, and how to avoid these problems.
In this article you will learn the following:
- What is a Contract and how to use it?
- Use of Kotlin annotations in projects?
- A line of code that accepts arguments passed by an Activity or Fragment?
- One line of code to pass parameters between activities?
- One line of code to pass parameters between fragments?
- One line of code to implement click events to avoid memory leaks?
KtKit warehouse
This article introduces some Kotlin techniques around a new library, KtKit, written in the Kotlin language. KtKit is a collection of tools commonly used in projects and is a complement to the Jetpack KTX series. It covers many techniques learned from Kotlin source code, Jetpack KTX, ANko, and other well-known open source projects, including Kotlin delegate attributes, high order functions, extension functions, inlining, use of annotations, and more.
- KtKit warehouse address: https://github.com/hi-dhl/KtKit
- KtKit can be read online at https://ktkit.hi-dhl.com
If you want to use the API in this article, you need to add the following code to the build.gradle file at the module level. Please check the version record for the latest version number.
implementation "com.hi-dhl:ktkit:${ktkitVersion}"
Copy the code
Due to the length of this article, the source code analysis will not be covered too much. The source code will be shared in a future article.
What is a Contract and how to use it
Kotlin is known to be smart, such as the Smart Cast feature, but in some cases it is clumsy and not so smart, as shown below.
public inline fun String? .isNotNullOrEmpty(): Boolean { return this ! = null && ! this.trim().equals("null", true) && this.trim().isNotEmpty() } fun testString(name: String?) { if (name.isNotNullOrEmpty()) { println(name.length) // 1 } }Copy the code
As you can see, only if the string name is not empty will it go into comment 1, but the above code doesn’t compile properly, as shown in the figure below.
The compiler will tell you a compilation error, and the code will analyze it and only enter comment 1 if the string name is not empty, but the compiler can’t deduce it properly. Is the compiler really unable to do this? See how the official documentation explains it.
However, as soon as these checks are extracted in a separate function, all the smartcasts immediately disappear:
Extract the checks into a function, and the effects of Smart Cast disappear
The compiler can’t analyze every function in depth because in actual development we might write more complex code, and the Kotlin compiler does a lot of static analysis. If the compiler analyzes every function, it takes time to analyze the context, increasing the time it takes to compile.
If you want to solve the appeal problem, you need to use the Contract feature. Contract is a very useful feature provided by Kotlin. The Contract is used when the Kotlin compiler doesn’t have enough information to analyze the situation of the function. Contracts can provide additional information for a function to help the Kotlin compiler analyze the function and modify the code as shown below.
inline fun String? .isNotNullOrEmpty(): Boolean { contract { returns(true) implies (this@isNotNullOrEmpty ! = null) } return this ! = null && ! this.trim().equals("null", true) && this.trim().isNotEmpty() } fun testString(name: String?) { if (name ! = null && name.isNotNullOrEmpty()) { println(name.length) // 1 } }Copy the code
If the return value is true, the object to which this refers will not be null. If the value is true, the object to which this refers will not be null. The Contract feature is heavily used in the Kotlin standard Library. Go to KtKit/ profileActively.kt for use of the above examples.
Use of Kotlin annotations in projects
Contract is an experimental API added by Kotlin 1.3. If we call the experimental API we need to add the @experimentalContracts annotation to make it work, but if we add the @experimentalContracts annotation, All calls to this method need to be annotated if you want to solve this problem. Simply add the following code to the first line of the contract declaration file.
@file:OptIn(ExperimentalContracts::class)
Copy the code
In the example above, the inline modifier is used, but the compiler gets a yellow warning, as shown in the figure below.
The compiler recommends that we use Inline when we take a function as an argument. Inline is used to improve performance. Calling a function with the Inline modifier will place a section of code inside the method on the call.
Since the Inline modifier can improve performance, why is the warning given that the misuse of the Inline modifier has a performance penalty? More on the performance penalty caused by the Inline modifier.
The Inline modifier is often used when the compiler does not have a warning:
- Taking functions as arguments (e.g., lambda expressions)
- Used with the Reified type parameter
In a normal method, however, the compiler will warn you if you use the Inline modifier. If you have a very short code segment in the method body and you want to improve performance by using the Inline modifier (albeit marginally), you can eliminate the warning by adding the following code to the first line of the file.
@file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER")
Copy the code
Then add the following comment where the Inline modifier is used and use it happily.
@kotlin.internal.InlineOnly
Copy the code
The role of the annotation @ kotlin. Internal. InlineOnly:
- Remove compiler warnings
- Change the visibility of inline functions to private at compile time
Public static final void showShortToast(@notnull Context $this$showShortToast, @NotNull String message) { ...... Toast.makeText($this$showShortToast, (CharSequence)message, 0).show(); @inlineOnly private static final void showShortToast(Context $this$showShortToast, Context $this$showShortToast) String message) { ...... Toast.makeText($this$showShortToast, (CharSequence)message, 0).show(); }Copy the code
A complete use case for annotations can be found in the KtKit repository.
A line of code accepts arguments passed by the Activity or Fragment
If you want to implement a line of code that accepts parameters passed by an Activity or Fragment, you can do so using the Kotlin delegate property. There are two apis provided in the repository KtKit. For example, go to KtKit/ profileActivity.kt.
class ProfileActivity : Activity() {private val userPassword by Intent <String>(KEY_USER_PASSWORD) Private val userName by intent<String>(KEY_USER_NAME) {" ByteCode: ByteCode"}}Copy the code
One line of code passes parameters between activities
This idea is a reference to the implementation of ANko, also provides two APIS, according to the actual situation can be used, you can pass any parameter supported by Android, the case can go to KtKit/ProfileActivity. Kt.
// API: activity.startActivity<ProfileActivity> { arrayOf( KEY_USER_NAME to "ByteCode" ) } activity.startActivity<ProfileActivity>( KEY_USER_NAME to "ByteCode" ) // Example: class ProfileActivity : Activity() { ...... companion object { ...... StartActivity <ProfileActivity> {arrayOf(KEY_USER_NAME to "ByteCode", arrayOf(KEY_USER_NAME to "ByteCode", StartActivity <ProfileActivity>(KEY_USER_NAME to "ByteCode", KEY_USER_PASSWORD to "1024" ) } }Copy the code
Pass parameters and results between activities
/ / a context. StartActivityForResult < ProfileActivity > (KEY_REQUEST_CODE KEY_USER_NAME to "the ByteCode", KEY_USER_PASSWORD to "1024") / / way ii context. StartActivityForResult < ProfileActivity > (KEY_REQUEST_CODE) {arrayOf ( KEY_USER_NAME to "ByteCode", KEY_USER_PASSWORD to "1024" ) }Copy the code
Back the result
SetActivityResult (activity.result_OK) {arrayOf(KEY_RESULT to "success", SetActivityResult (activity.result_OK, KEY_RESULT to "success", KEY_RESULT to "ByteCode")} KEY_USER_NAME to "ByteCode" )Copy the code
One line of code to pass parameters between fragments
The same as the Activity provides two API according to the actual situation can be used, you can pass any parameter supported by Android, source to check KtKit/ loginfragment.kt.
// API: LoginFragment().makeBundle( KEY_USER_NAME to "ByteCode" ) LoginFragment().makeBundle { arrayOf( KEY_USER_NAME to "ByteCode" ) } // Example: class LoginFragment : Fragment(R.layout.fragment_login) { ...... companion object { ...... // Way 1 fun newInstance1(): Fragment { return LoginFragment().makeBundle( KEY_USER_NAME to "ByteCode", KEY_USER_PASSWORD to "1024")} funwinstance2 (): Fragment { return LoginFragment().makeBundle { arrayOf( KEY_USER_NAME to "ByteCode", KEY_USER_PASSWORD to "1024" ) } } } }Copy the code
A line of code to achieve click events to avoid memory leaks
KtKit provides three commonly used apis: click event, delay the first click event, and prevent multiple clicks
Click event
View. Click (lifecycleScope) {showShortToast(" public account: ByteCode")}Copy the code
Delay the first click event
ClickDelayed (lifecycleScope){showShortToast(" public number: // or view. ClickDelayed (lifecycleScope, 1000){showShortToast(" public number: ByteCode")}Copy the code
Prevent multiple clicks
ClickTrigger (lifecycleScope){showShortToast(" public number: // or view.clicktrigger (lifecycleScope, 1000){showShortToast(" public account: ByteCode")}Copy the code
But View#setOnClickListener causes memory leaks, and those of you who have done performance tuning should see a lot of these cases.
Kotlin Flow provides a very useful API callbackFlow. The source code is shown below.
fun View.clickFlow(): Flow<View> {
return callbackFlow {
setOnClickListener {
safeOffer(it)
}
awaitClose { setOnClickListener(null) }
}
}
Copy the code
CallbackFlow converts a callback into a flow, and awaitClose is executed at the end of the flow.
So when does flow end execution
In the source code, I use the lifecycleScope to bind Flow to the Activity/Fragment lifecycle. At the end of the Activity/Fragment lifecycle, When flow ends, the Listener is set to NULL, effectively avoiding memory leaks. The source code is shown below.
inline fun View.click(lifecycle: LifecycleCoroutineScope, noinline onClick: (view: View) -> Unit) {
clickFlow().onEach {
onClick(this)
}.launchIn(lifecycle)
}
Copy the code
conclusion
KtKit is a tool library written in Kotlin language, which contains a series of commonly used tools in the project. However, it is not perfect yet. Some commonly used functions are being gradually combined with the features of Kotlin’s high-level functions, which not only makes the code more readable and easier to use. It also helps us solve common problems in projects.
KtKit warehouse address: https://github.com/hi-dhl/KtKit KtKit online reading: https://ktkit.hi-dhl.com
The spotless plugin is referenced in the project, and executing./gradlew spotlessApply will format the code from Java, Kotlin, XML, Gradle, MD, gitignore, etc. This is also how Google recommends when submitting code.
This is the end of the full text, if this warehouse is helpful to you, please help me star in the upper right corner of the warehouse, thank you very much for your support, and you are also welcome to submit PR ❤️❤️❤️
If it helps, a “like” is the biggest encouragement to me
The code doesn’t stop, the article doesn’t stop
Welcome to the public account: ByteCode, continue to share the latest technology
Finally, I recommend the projects and websites THAT I have been updating and maintaining:
-
This is a personal blog post: this post is categorized
-
We are planning to build a complete and up-to-date practical project of AndroidX Jetpack-related components and analysis articles of related components. We are gradually adding new members of Jetpack, and the warehouse is continuously updated. Welcome to check out: AndroidX Jetpack-Practice
-
LeetCode/Jian Point Offer/domestic and foreign big factory interview questions/multi-threaded problem solving, language Java and Kotlin, including a variety of solutions, problem solving ideas, time complexity, space complexity analysis
- Jian refers to offer and domestic and foreign dafang interview questions: online reading
- LeetCode series of questions: online reading
-
The latest Android10 Source code Analysis series articles, understand the system Source code, not only help to analyze the problem, in the interview process, is also very helpful to us, the warehouse continues to update, welcome to check Android 10-source-Analysis
-
Collate and translate a series of selected foreign Technical articles, each Article will have a translator’s thinking part, more in-depth interpretation of the original text, the warehouse continues to update, welcome to check Technical articles-translation
-
“Designed for the Internet people, domestic and foreign famous station navigation” includes news, sports, life, entertainment, design, product, operation, front-end development, Android development and so on website, welcome to go to see for the Internet people and design navigation website
Article history
- Uncover == and === in Kotlin
- The Kotlin seal class evolved
- Sealed classes in Kotlin are superior to labeled classes
- What’s Kotlin Sealed? Why does Google use it
- What does LeetCode learn from 0 to 200
- What’s Kotlin Sealed? Why does Google use it
- Android 12 behavior changes and their impact on applications
- Multi-platform AndroidStudio tips (3)
- Kotlin technique and principle analysis that few people know (1)
- Kotlin technique and principle analysis that few people know (2)
- Kotlin StateFlow search functionality in practice DB + NetWork
- The end of the Kotlin plugin, the rise of ViewBinding
- It’s surprisingly simple, DataBinding and ViewBinding