Using kotlin synergy to improve app performance

Coroutine is a concurrent design pattern that you can use on Android to simplify asynchronously executed code. Kotlin version 1.3 adds coroutines and is based on established concepts in other languages.

On Android, synergy helps solve two main problems:

This topic describes how to use the kotlin collaboration to solve these problems, so that you can write clearer and simpler application code.

Manage long-running tasks

On Android, each application has a main thread to handle the user interface and manage user interaction. If your application allocates too much work to the main thread, the application may get stuck or run slowly. Network requests, JSON parsing, reading or writing from the database, or even just iterating over large lists may cause applications to run slowly, resulting in slow visible or frozen UI response to touch events. These long-running operations should run outside the main thread.

The following example shows a simple co process implementation of a hypothetical long-running task:

suspend fun fetchDocs() {        // Dispatchers.Main
 val result = get("https://developer.android.com") // Dispatchers.IO for `get`
 show(result)          // Dispatchers.Main
}

suspend fun get(url: String) = withContext(Dispatchers.IO) { /* ... */ }

Cooperative programs build general functions by adding two operations to deal with long-running tasks. In addition to invoke (or call) and return, the collaboration program also adds suspend and resume:

You can only call the suspend function from other suspend functions, or use a coroutine builder such as startup to start a new coroutine.

In the above example, get () still runs on the main thread, but it suspends the collaborator before starting the network request. When the network request is completed, get resumes the suspended orchestration instead of using a callback to notify the main thread.

Kotlin uses a stack framework to manage functions that run with any local variable. When a coroutine is suspended, the current stack frame is copied and saved for later use. On recovery, the stack frame is copied back from the saved location and the function starts running again. Even if the code looks like a normal sequential blocking request, the coroutine can ensure that the network request does not block the main thread.

Use coroutines for main-safety

The kotlin coroutine uses a scheduler to determine which threads are used for coroutine execution. To run code outside the main thread, you can tell the kotlin coroutine to perform work on the default or IO scheduler. In kotlin, all collaborators must run in the scheduler, even if they run on the main thread. Collaborators can be suspended and the scheduler is responsible for restoring them. To specify where the collaboration should run, kotlin provides three schedulers that can be used:

Continuing with the previous example, you can redefine the get function using the scheduler. Inside the body of get, call withcontext (dispatchers. IO) to create a block running on the IO thread pool. Any code placed in this block is always executed through the IO scheduler. Because withcontext itself is a suspended function, the function get is also a suspended function.

Using a coroutine, you can schedule threads with fine-grained control. Because withcontext () allows you to control the thread pool of any line of code without introducing callbacks, you can apply it to very small functions, such as reading from a database or executing network requests. A good practice is to use withcontext () to ensure that each function is safe, which means that you can call the function from the main thread. In this way, the caller never needs to consider which thread should be used to execute the function.

In the previous example, fetchdocs () executes on the main thread; However, it can safely call get, which performs network requests in the background. Because the coroutine supports suspension and recovery, as long as the withcontext block is completed, the coroutine on the main thread will be recovered with the get result.

Compared with the equivalent callback based implementation, withcontext () does not add additional overhead. In addition, in some cases, the withcontext () call can be optimized rather than based on an equivalent callback based implementation. For example, if a function makes ten calls to the network, you can tell kotlin to switch threads only once by using the external withcontext(). Then, even if the network library uses withcontext () multiple times, it still stays on the same scheduler and avoids switching threads. In addition, kotlin optimizes the switching between dispatchers.default and dispatchers.io to avoid thread switching as much as possible.

Specify coroutinescope

When defining a collaboration, you must also specify its coroutinescope. Coroutinescope manages one or more related processes. You can also use coroutine scope to start a new collaboration within this scope. However, unlike schedulers, coroutinescope does not run collaborators.

An important function of coroutine scope is to stop the co process execution when the user leaves the content area in the application. With coroutinescope, you can ensure that any running operations are stopped correctly.

Use coroutinescope with Android architecture components

On Android, you can associate the coroutinescope implementation with the component lifecycle. This avoids leaking memory or performing additional work for activities or fragments that are no longer relevant to the user. Using jetpack components, they are naturally suitable for ViewModel. Because the ViewModel is not destroyed during configuration changes, such as screen rotation, you don't have to worry about the collaborator being cancelled or restarted.

Scope knows every collaborative program they start. This means that you can cancel everything started in the scope at any time. The scope propagates itself, so if one collaborative process starts another collaborative program, the two collaborative programs have the same scope. This means that you can cancel other libraries at any time even if they start collaborations from your scope. This is especially important if you are running the collaborator in the ViewModel. If the ViewModel is destroyed because the user leaves the screen, all asynchronous work it is performing must be stopped. Otherwise, you will waste resources and may leak memory. If you should continue asynchronous work after destroying the ViewModel, it should be done at a lower level of the application architecture.

Using the KTX library component for Android architecture, you can also use the extended property viewmodelscope to create cooperative programs that can run until the ViewModel is destroyed.

Start a collaborative process

You can start a collaboration program in one of two ways:

Usually, you should start a new coroutine from a regular function, because a regular function cannot call wait. Asynchrony is used only when parallel decomposition is performed inside another collaborator or within a suspended function.

Based on the previous example, here is a collaboration with viewmodelscope KTX extension attribute. It uses launch to switch from a regular function to a collaboration program:

fun onDocsNeeded() {
 viewmodelScope.launch { // Dispatchers.Main
  fetchDocs()   // Dispatchers.Main (suspend function call)
 }
}

Warning: startup and asynchronous handling of exceptions are different. Because async expects to eventually call await at some point, it keeps exceptions and re throws them in the await call. This means that if you use await to start a new collaborator from a regular function, exceptions may be silently removed. These discarded exceptions will not appear in the crash indicator or in logcat.

Parallel decomposition

When the function returns, all collaborators started by the suspended function must be stopped, so you may need to ensure that these collaborations are completed before returning. With structured concurrency in kotlin, you can define a coroutine scope that starts one or more collaborators. Then, using await () (for a single co program) or awaitall () (for multiple co procedures) ensures that these co procedures are completed before returning from the function.

For example, let's define a coroutine scope that gets two documents asynchronously. By calling await () on each deferred reference, we ensure that both asynchronous operations are completed before the return value:

suspend fun fetchTwoDocs() =
 coroutineScope {
  val deferredOne = async { fetchDoc(1) }
  val deferredTwo = async { fetchDoc(2) }
  deferredOne.await()
  deferredTwo.await()
 }

Even if fetchtwodocs() starts new collaborators asynchronously, the function uses awaitall() to wait for those started collaborators to complete before returning. However, please note that even if we do not call awaitall(), the coroutine scope builder will not resume the process of calling fetchtwodocs until all new processes are completed.

In addition, coroutinescope captures any exceptions thrown by the orchestration and routes them back to the user.

For more information about parallel decomposition, see writing suspend functions.

Architecture components with built-in support

Some architecture components, including ViewModel and lifecycle, include built-in support for collaborative applications through their own coroutine scope members. For example, ViewModel contains a built-in viewmodelscope. This provides a standard way to start collaborative programs within the scope of ViewModel, as shown in the following example:

class Myviewmodel : viewmodel() {

 fun launchdataLoad() {
  viewmodelScope.launch {
   sortList()
   // Modify UI
  }
 }

 /**
 * Heavy operation that cannot be done in the Main Thread
 */
 suspend fun sortList() = withContext(Dispatchers.Default) {
  // Heavy work
 }
}

LiveData还使用带有liveData块的协同程序:
liveData {
 // runs in its own LiveData-specific scope
}

original text

The above is the whole content of this article. I hope it will help you in your study, and I hope you will support us a lot.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>