What is the Kotlin coroutine?
A coroutine is a concurrent design pattern and a Kotlin coroutine is a threading framework.
Why do WE need a Kotlin coroutine?
Provide convenient thread operation API, write logical and concise thread code.
Coroutines are Google’s recommended solution for asynchronous programming on Android. It has the following characteristics:
- Lightweight: You can run multiple coroutines on a single thread because coroutines support suspension and do not block the thread running them. Suspension saves memory than blocking and supports multiple parallel operations.
- Fewer memory leaks: Multiple operations are performed within a scope using structured concurrency mechanisms.
- Built-in cancel support: Cancel operations are automatically propagated throughout the running coroutine hierarchy.
- Jetpack Integration: Many Jetpack libraries include extensions that provide full coroutine support. Some libraries also provide their own coroutine scope that you can use to structure concurrency.
How do I use the Kotlin coroutine
Add the dependent
dependencies {
implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines - android: 1.3.9'
}
Copy the code
Usage scenarios
Start the coroutines
If we need to perform a simple background (or foreground) task, we can quickly launch a coroutine to handle the business logic via GlobalScope.launch. We can also specify the thread type via Dispatchs: Dispatchers.Default, dispatchers. IO, dispatchers. Main, dispatchers. Unconfined.
GlobalScope.launch(Dispatchers.IO) {
delay(1000)
Log.d(TAG, "processIO in ${Thread.currentThread().name}")}Copy the code
Switch threads
Main thread => IO thread => main thread. This scenario is most commonly used in development, such as the background to get a photo, and then the foreground display.
// start a coroutine in the main thread
GlobalScope.launch(Dispatchers.Main) {
// Switch to the IO thread
withContext(Dispatchers.IO) {
delay(1000)
Log.d(TAG, "processIO in ${Thread.currentThread().name}")}// Automatically cut back to the main thread
Log.d(TAG, "processUI in ${Thread.currentThread().name}")}Copy the code
Running results:
The 2021-01-02 18:38:23. 812, 15506-15535 / tech. Kicky. The coroutine D/coroutine Sample: processIOinDefaultDispatcher - 2021-01-02 18:38:23 worker - 1. 813, 15506-15506 / tech. Kicky. The coroutine D/coroutine Sample: processUIin main
Copy the code
In disappear assist cheng
private fun cancelCoroutine(a) {
val job = GlobalScope.launch(Dispatchers.IO) {
for (i in 0.10000.) {
delay(1)
Log.d(TAG, "count = $i")
}
}
Thread.sleep(30)
job.cancel()
Log.d(TAG, "Coroutine Cancel")}Copy the code
The result is as follows:
The 2021-01-02 18:53:37. 680, 23240-23279 / tech. Kicky. The coroutine D/coroutine Sample: Count = 0 2021-01-02 18:53:37. 682, 23240-23278 / tech. Kicky. The coroutine D/coroutine Sample: Count = 1 2021-01-02 18:53:37. 685, 23240-23280 / tech. Kicky. The coroutine D/coroutine Sample: Count = 2 2021-01-02 18:53:37. 687, 23240-23280 / tech. Kicky. The coroutine D/coroutine Sample: Count = 3 2021-01-02 18:53:37. 689, 23240-23280 / tech. Kicky. The coroutine D/coroutine Sample: Count = 4 18:53:37. 2021-01-02, 690, 23240-23280 / tech. Kicky. The coroutine D/coroutine Sample: Count = 5 18:53:37. 2021-01-02, 693, 23240-23280 / tech. Kicky. The coroutine D/coroutine Sample: Count = 6 2021-01-02 18:53:37. 696, 23240-23240 / tech. Kicky. The coroutine D/coroutine Sample: coroutine CancelCopy the code
LifecycleOwner works with it
The Kotlin coroutine is mainly used to process thread operations. If the coroutine is not properly processed, memory leaks may occur. For example, activities or fragments are destroyed but the coroutine in the interface is still being executed. Therefore, we must cancel the coroutine operation in the interface when we destroy it. We can call the cancel() method ourselves at interface destruction, but it’s easy to ignore without good programming habits. We recommend using the extension method provided by Google.
implementation "Androidx. Activity: activity - KTX: 1.1.0." "
implementation "Androidx. Fragments: fragments - KTX: 1.2.5." "
Copy the code
private fun lifecycleCoroutine(a) {
// start a coroutine in the main thread
lifecycleScope.launch {
// Switch to the IO thread
withContext(Dispatchers.IO) {
delay(1000)
Log.d(TAG, "processIO in ${Thread.currentThread().name}")}// Automatically cut back to the main thread
Log.d(TAG, "processUI in ${Thread.currentThread().name}")}}Copy the code
Note:
LifecycleScope. Launch () defaults to start the coroutine on the main thread;
2. Coroutines in lifecycleScope will be cancelled automatically when Lifecycle is in a destroyed state.
3. LifecycleScope has several other extensions, such as launchWhenCreated, launchWhenStarted, and launchWhenResumed
ViewModel is used together
Coroutines work with LifecycleOwner to solve the problem of handling coroutines during interface lifecycle changes. But for scenarios where the screen is rotated and the interface is reconstructed, the ViewModel object lasts longer than the LifecycleOwner. Although the interface needs to be rebuilt, the coroutine does not have to be cancelled, and this needs to be considered for specific needs.
fun viewModelCoroutine(a) {
viewModelScope.launch {
Log.d("Coroutine Sample", Thread.currentThread().name)
}
}
Copy the code
Note:
Viewmodelscope.launch () also starts the coroutine on the main thread by default;
2. Coroutines in viewModelScope are automatically cleared when the ViewModel is about to be onCleared.
Retrofit really smells good
To get to the point, versions after Retrofit 2.6 support using Kotlin’s coroutines. So, how to support?
- Add the dependent
implementation "Com. Squareup. Retrofit2: retrofit: 2.9.0"
implementation "Com. Squareup. Retrofit2: converter - gson: 2.9.0"
Copy the code
- Adding Network Rights
<uses-permission android:name="android.permission.INTERNET" />
Copy the code
- Retrofit
object Retrofitance {
private val client: OkHttpClient by lazy {
OkHttpClient.Builder()
.build()
}
private val retrofitance: Retrofit by lazy {
Retrofit.Builder()
.baseUrl("https://www.wanandroid.com")
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build()
}
val wanAndroidApi: WanAndroidApi by lazy {
retrofitance.create(WanAndroidApi::class.java)
}
}
Copy the code
- API
interface WanAndroidApi {
@GET("/banner/json")
suspend fun banners(a): WanAndroidRoot<Banner>
}
Copy the code
Focus on the suspend keyword in the API. Suspend means suspend, reminding developers that this method is a time-consuming method.
- Executing network requests
class MainViewModel : ViewModel() {
val banners = MutableLiveData<List<Banner>>()
fun viewModelCoroutine(a) {
viewModelScope.launch(Dispatchers.IO) {
val result = Retrofitance.wanAndroidApi.banners()
banners.postValue(result.data)}}}Copy the code
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
setContentView(binding.root)
viewModel.banners.observe(this, {
val content: List<String> = it.map { banner ->
banner.title
}
binding.text.text = content.toTypedArray().contentToString()
})
viewModel.viewModelCoroutine()
}
Copy the code
Retrofit requests dependencies
For network requests with dependencies, we need to deal with them in callback before using coroutines. One layer and two layers are ok, but it is easy to be messy if there are more layers. Use coroutines, write code sequentially, concise and clear.
fun viewModelSequenceRequest(a) {
viewModelScope.launch(Dispatchers.IO) {
val start = System.currentTimeMillis()
// Make a banner request first
val result = Retrofitance.wanAndroidApi.banners()
banners.postValue(result.data)
// Request hotkeys again, as long as they are executed sequentially and the result of the previous request has been obtained, it can satisfy our usage scenario.
val keys = Retrofitance.wanAndroidApi.hotKeys()
hotKeys.postValue(keys.data)
Log.d("Coroutine Sample", (System.currentTimeMillis() - start).toString())
}
}
Copy the code
Retrofit concurrent results merge
For multiple concurrent executions, the results are processed uniformly, and then something else is executed. Before using coroutines, we can use RxJava’s ZIP operator. Coroutines async/await are easily competent.
fun viewModelAsync(a) {
viewModelScope.launch(Dispatchers.IO) {
val start = System.currentTimeMillis()
val result = async { Retrofitance.wanAndroidApi.banners() }
val keys = async { Retrofitance.wanAndroidApi.hotKeys() }
Log.d(
"Coroutine Sample",
(result.await().data.size + keys.await().data.size).toString()
)
Log.d("Coroutine Sample", (System.currentTimeMillis() - start).toString())
}
}
Copy the code
The code is the same as in the previous example, but the execution time is much shorter because it is executed concurrently rather than sequentially.
conclusion
In my opinion, the basic use of Kotlin coroutines focuses on the following three aspects:
- thread
- How to avoid memory leaks (in conjunction with LifecycleOwner, ViewModel, etc.)
- Collocation to Retrofit
Of course, there are many more operators and methods in the Kotlin Coroutine library that need to be explored immediately.
The source code
Github.com/onlyloveyd/…
Kotlin Couroutine source
Pay attention to my
OpenCV or Android
Reply [computer vision] [Android] [Flutter] [OpenCV] white whoring learning materials.