Kotlin detailed article notes collation update progress: Kotlin Series – High order functions and Common functions in standard Library (3) Kotlin Series – Advanced Deep Generic Covariant Inversion from Java to Kotlin(4)
preface
I’ve studied Kotlin for a whole series, but I haven’t put together a blog post on coroutines. Alas, recent state is a bit wrong > _ < | |. But come on anyway!! The last one should end with a perfect ending and a beautiful flower.
One of the things about coroutines is that just to give you an idea, coroutines are not esoteric things, they’re actually threads, they’re not a new concept that comes out of thin air. The official website may sound a bit lofty, but you can actually think of it as an API for us to use the thread pool to encapsulate some logic with the Handler to automatically switch threads
Some basic uses of coroutines
Add basic dependencies
implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines -- core: 1.3.0'
implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines - android: 1.3.0'
Copy the code
GlobalScope
Official website definition: Global scope is used to launch top-level coroutines which are operating on the whole application lifetime and are not cancelled prematurely. Another use of the global scope is operators running in Dispatchers.Unconfined, Which don’t have any job associated with them. Application code usually should use an application-defined CoroutineScope. Using async or launch on the instance of GlobalScope is highly discouraged. This is generally used for top-level coroutines, application lifecycle level, and will not be cancelled prematurely. Applications should generally use an application-defined CoroutineScope. Instances of asynchrony or startup GlobalScope are discouraged (not recommended).
Let’s simulate a scenario where globalScopeLaunch is called from an ActivityA, or globalScopeLaunch is called to perform a time-consuming operation, such as an IO operation or a network request. Then destroy ActivityA and jump to ActivityB before it returns
fun globalScopeLaunch(){
GlobalScope.launch(Dispatchers.Main) {
delay(5000)
Toast.makeText(this@MainActivity,"Wait five seconds for eject ~~~",Toast.LENGTH_LONG).show()
}
}
Copy the code
You’ll notice that it still pops up the Toast, but that’s not what we want. There are transactions that we should end with the end of the component’s life cycle. Otherwise, resources will be wasted or memory will leak. (The problem is that you can avoid this if you turn it off manually at the end of the life cycle. But this involves manually controlling it yourself.)
private fun globalScopeLaunch1(){
GlobalScope.launch(Dispatchers.Main) {
launch (Dispatchers.Main){
delay(1000)
Toast.makeText(this@MainActivity,"Wait one second to eject.",Toast.LENGTH_LONG).show()
}
Toast.makeText(this@MainActivity,"Pop it right away.",Toast.LENGTH_LONG).show()
delay(5000)
Toast.makeText(this@MainActivity,"Wait five seconds for eject ~~~",Toast.LENGTH_LONG).show()
}
}
Copy the code
In GlobalScope Aunch1, eject immediately ~~~ -> wait one second to eject ->” Wait five seconds to eject ~~~. Here will pop up immediately pop up the message ~~~. Because in the coroutine, a new coroutine is opened, and the new coroutine blocks for a second and nothing happens to the outside coroutine, and the outside coroutine continues.
private fun globalScopeLaunch(){job = globalscope.launch (dispatchers.main){launch(dispatchers.main){runBlocking {// add runBlocking to the applet scope delay(1000) Toast.makeText(this@MainActivity,"Wait one second to eject.",Toast.LENGTH_LONG).show()
}
}
Toast.makeText(this@MainActivity,"Pop it right away.",Toast.LENGTH_LONG).show()
delay(5000)
Toast.makeText(this@MainActivity,"Wait five seconds for eject ~~~",Toast.LENGTH_LONG).show()
}
}
Copy the code
– runBlocking causes the Toast to pop up immediately. Instead of showing it immediately, it will wait 1 second before popping up again.
The code above can be simplified to use withContext(dispatchers.main) instead of launch (dispatchers.main) in the coroutine scope.
job = GlobalScope.launch(Dispatchers.Main) {
withContext(Dispatchers.Main){}
}
Copy the code
Coroutine scope
Globalscope.launch (dispatchers.main) is sent to the Main thread for delay but does not cause ANR
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
Copy the code
The third argument: block: suspend CoroutineScope.() means that CoroutineScope is CoroutineScope used and does not block. Blocking here refers to code blocking outside the coroutine scope, and can be blocked inside it. CoroutineScope is a parent of GlobalScope
The launch modes of coroutines are launch and Async
private fun globalScopeAsync(){
GlobalScope.launch(Dispatchers.Main){
val deferred = async(IO) {
Thread.sleep(5000)
"Wait five seconds for eject ~~~"
}
Toast.makeText(this@MainActivity,"Pop it out right now.", toast.length_long).show()// Val message = deferred.await() where sync does not block code outside the async coroutine Toast.makeText(this@MainActivity,message,Toast.LENGTH_LONG).show() } }Copy the code
Async will asynchronously run the logic of the coroutine outside the scope. We can see that the “pop up now first ~~” popup box will pop up first, and then” wait five seconds to pop up “popup box will pop up again after five seconds. In await it blocks waiting for the Deferred to return before continuing.
Coroutines distribution
Cancellation of coroutines
Get the corresponding coroutine Job object, call Cancel ()
Var job = globalscope.launch (dispatchers.main) {} job.cancel() // The object of Deferred is job var Deferred = globalscope.async {} deferred.cancel()Copy the code
The correct posture for using coroutines on Android
MainScope
The official definition of Global above prompts us to use a custom coroutine. MainScope is a coroutine scope that Kotlin has customized for us. Code definition:
@Suppress("FunctionName")
public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)
Copy the code
Basic use:
Class MyAndroidActivity {private val scope = MainScope() // Val MainScope = MainScope() + CoroutineName(this.javaClass.name) private funmainScopeLaunch(){
scope.launch {}
}
override fun onDestroy() {
super.onDestroy()
scope.cancel()
}
}
Copy the code
You can put this logic in the Base class
/ / don't need to define coroutines name when the open class BaseCoroutineScopeActivity: AppCompatActivity() , CoroutineScope by MainScope() class MainActivity :BaseCoroutineScopeActivity(){
private fun mainScopeLaunch(){
launch { }
}
override fun onDestroyCancel () {super. OnDestroy () ()}} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- / / define coroutines name when open class BaseCoroutineScopeActivity :AppCompatActivity() {
val mainLaunch = MainScope()+ CoroutineName(this.javaClass.simpleName)
}
class MainActivity : BaseCoroutineScopeActivity(){
private fun mainScopeLaunch(){
mainLaunch.launch { }
}
override fun onDestroy() {
super.onDestroy()
mainLaunch.cancel()
}
}
Copy the code
ViewModelScope
Using the coroutine starts by importing packages
api 'androidx. Lifecycle: lifecycle - viewmodel - KTX: 2.2.0 - rc02'
Copy the code
Code definition:
private const val JOB_KEY = "androidx.lifecycle.ViewModelCoroutineScope.JOB_KEY"
val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if(scope ! = null) {return scope
}
return setTagIfAbsent(JOB_KEY, CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)) } internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope { override val coroutineContext: CoroutineContext = context override funclose() {
coroutineContext.cancel()
}
}
Copy the code
This code is the same as MainScope, except that the CloseableCoroutineScope package is added. Let’s go in and look at setTagIfAbsent
<T> T setTagIfAbsent(String key, T newValue) {
T previous;
synchronized (mBagOfTags) {
previous = (T) mBagOfTags.get(key);
if (previous == null) {
mBagOfTags.put(key, newValue);
}
}
T result = previous == null ? newValue : previous;
if (mCleared) {
closeWithRuntimeException(result);
}
return result;
}
Copy the code
So you can see here that when mCleared = true it will automatically close viewModelScope, which means it will help us with the lifecycle and we can just use it.
Use:
fun requestAhuInfo() {
viewModelScope.launch { }
}
Copy the code
I don’t use LiveData && LifecycleScope myself.
recommend
Bing Xin says TM – How to use coroutines correctly on Android? There are some coroutines that Kotlin provides for us
Multiple tasks in coroutines
- Launch + withContext multiple tasks
viewModelScope.launch {
var result1 = withContext(Dispatchers.IO) {
Log.i(TAG, "result1-1")
Log.i(TAG, "result1-2")
Thread.sleep(4000)
Log.i(TAG, "result1-3")
"Hello"
}
var result2 = withContext(Dispatchers.IO) {
Log.i(TAG, "result2-1")
Log.i(TAG, "result2-2")
Log.i(TAG, "result2-3")
"world"} val result = result1 + result2 Log.i(TAG, Result)} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the 2020-03-23 19:19:40. 587 11021-11096/com.ldr.testcoroutines I/MainViewModel: The 2020-03-23 19:19:40 result1-1. 587, 11021-11096 / com. The LDR. Testcoroutines I/MainViewModel: Result1-2 2020-03-23 19:19:44. 592, 11021-11096 / com. The LDR. Testcoroutines I/MainViewModel: Result1-3 19:19:44. 2020-03-23, 602, 11021-11096 / com. LDR. Testcoroutines I/MainViewModel: The 2020-03-23 19:19:44 result2-1. 603, 11021-11096 / com. The LDR. Testcoroutines I/MainViewModel: Result2-2 2020-03-23 19:19:44. 603, 11021-11096 / com. The LDR. Testcoroutines I/MainViewModel: Result2-3 19:19:44. 2020-03-23, 603, 11021-11021 / com. LDR. Testcoroutines I/MainViewModel: the HelloworldCopy the code
- (Launch + async) (launch+ launch)
viewModelScope.launch {
val deferred = async(Dispatchers.IO) {
Log.i(TAG, "result1-1")
Log.i(TAG, "result1-2")
Thread.sleep(4000)
Log.i(TAG, "result1-3")
"Hello"
}
val deferred1 = async {
Log.i(TAG, "result2-1")
Log.i(TAG, "result2-2")
Log.i(TAG, "result2-3")
"world"} var str = deferred.await() + deferred1.await() Log.i(TAG, STR)} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the 2020-03-25 13:36:00. 736 6221-6253/com.ldr.testcoroutines I/MainViewModel: The 2020-03-25 13:36:00 result1-1. 736, 6221-6253 / com. The LDR. Testcoroutines I/MainViewModel: Result1-2 2020-03-25 13:36:00. 737, 6221-6221 / com. The LDR. Testcoroutines I/MainViewModel: The 2020-03-25 13:36:00 result2-1. 737, 6221-6221 / com. The LDR. Testcoroutines I/MainViewModel: Result2-2 2020-03-25 13:36:00. 737, 6221-6221 / com. The LDR. Testcoroutines I/MainViewModel: Result2-3 13:36:04. 2020-03-25, 738, 6221-6253 / com. LDR. Testcoroutines I/MainViewModel: Result1-3 13:36:04. 2020-03-25, 750, 6221-6221 / com. LDR. Testcoroutines I/MainViewModel: the HelloworldCopy the code
viewModelScope.launch {
launch(Dispatchers.IO) {
Log.i(TAG, "result1-1")
Log.i(TAG, "result1-2")
Thread.sleep(4000)
Log.i(TAG, "result1-3")
}
launch(Dispatchers.IO) {
Log.i(TAG, "result2-1")
Log.i(TAG, "result2-2")
Log.i(TAG, "result2-3")}} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the 2020-03-23 19:21:29. 781 11021-11128/com.ldr.testcoroutines I/MainViewModel: The 2020-03-23 19:21:29 result1-1. 781, 11021-11128 / com. The LDR. Testcoroutines I/MainViewModel: Result1-2 2020-03-23 19:21:29. 782, 11021-11096 / com. The LDR. Testcoroutines I/MainViewModel: The 2020-03-23 19:21:29 result2-1. 782, 11021-11096 / com. The LDR. Testcoroutines I/MainViewModel: Result2-2 2020-03-23 19:21:29. 782, 11021-11096 / com. The LDR. Testcoroutines I/MainViewModel: Result2-3 19:21:33. 2020-03-23, 782, 11021-11128 / com. LDR. Testcoroutines I/MainViewModel: result1-3Copy the code
Exception handling of coroutines
- CoroutineExceptionHandler
val handler = CoroutineExceptionHandler { _, exception ->
println("Caught $exception") } fun catchFun(): Unit { viewModelScope.launch(handler) { throw IOException() } } fun catch2Fun(): Unit { viewModelScope.launch(handler) { launch(Dispatchers.IO) { withContext(Dispatchers.Main){ throw IOException() } } }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - the 2020-03-23 19:57:21. 205 12038-12096/com.ldr.testcoroutines I/System.out: Caught Java. IO. IOException 19:59:23. 2020-03-23, 221, 12038-12096 / com. LDR. Testcoroutines I/System. Out: Caught java.io.IOExceptionCopy the code
Through the above test, can know CoroutineExceptionHandler under the method, multilayer can be nested exception also captured.
- try { }catch(){}
Funcatch1fun (): Unit { try { viewModelScope.launch(Dispatchers.Main) { throw IOException() } }catch (e:Exception){ Log.i(this.javaClass.name,e.cause?.message?:"Threw an exception.")}} // The correct way to write it ,,,, I feel like I'm talking nonsense... fun catch1Fun(): Unit { viewModelScope.launch(Dispatchers.Main) { try { throw IOException() }catch (e:Exception){ Log.i(this.javaClass.name,e.cause?.message?:"Threw an exception.")}}}Copy the code
Attached source code address: github.com/lovebluedan…
conclusion
The above is a brief introduction, some basic usage of coroutine, about many of the original things, later have the opportunity to write ~~ to tell the truth, I did not use very deep, so many details have not understood well. In the past, you can write abstruse points, less nonsense, less code, articles written to refine points ~~