Suspend official explanation:

Suspend is used to suspend execution of the current coroutine and save all local variables. The suspend function can only be called from another suspend function or by starting a new coroutine using a coroutine builder, such as launch.Copy the code

The authorities are as confused as they are not

website

2, look at the source code

Case 1

Create a code example directly in the Activity:

override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.d("LogUtils"."OnCreate execution begins")
        var job = GlobalScope.launch(Dispatchers.Main) {
            Log.d("LogUtils"."Main thread:" + Thread.currentThread())
            val asyncs = async(Dispatchers.IO) {
                Thread.sleep(50000)
                Log.d("LogUtils"."Child thread:" + Thread.currentThread())
                "Elapsed time completed"
            }
            Log.d("LogUtils"."Execute here.")
            Log.d("LogUtils", asyncs.await())
            Log.d("LogUtils"."Launch execution completed")
        }
        Log.d("LogUtils"."OnCreate execution completed")}Copy the code

Running results:

D/LogUtils: onCreate Execution starts D/LogUtils: onCreate execution ends D/LogUtils: main Thread: Thread[main,5,main] D/LogUtils: run here D/LogUtils: sub-thread: Thread[DefaultDispatcher-worker-1.5,main] D/LogUtils: time the execution ends D/LogUtils: launch The execution endsCopy the code

The main thread is not blocked, the launch{···} block is suspended and executed in the main thread, the async{···} block is suspended and executed in the child thread, and the code after asyncs.await() is suspended and executed in the main thread.

If you look at the source code implementation, you will see that these suspended execution operations are decorated with the suspend keyword

For example: Launch {···} source code:

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

Block: suspend CoroutineScope.() -> Unit receives launck{···} code blocks and is decorated by the suspend keyword, that is, {···} code blocks are defined as suspended modules.

See example 1, set the coroutine launch{···} as the main thread operation, after executing the sample code according to the log print result, it is found that although launch{···} is set as the main thread, the code block is not executed according to the main thread order, the launch{··· ·} code block is like suspending. Instead, it executes later and on the main thread, as if the launch{···} block is suspended, and at some point it cuts back to the main thread to continue executing the block’s code logic

Can methods decorated with the suspend keyword or blocks of code be suspended or suspended without any external Settings and then executed again at some point? (The answer is clearly no.)

Through coroutine source code and Kotlin source code to see the problem

kotlinx.coroutines.intrinsics.Cancellable.kt

...internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(
    receiver: R,                
    completion: Continuation<T> 
) =
    runSafely(completion) {
        createCoroutineUnintercepted(receiver, completion).intercepted()
            .resumeCancellableWith(Result.success(Unit}...))Copy the code

Kotlin. Coroutines. Intrinsics. IntrinsicsJvm. Kt (kotlin source class)

...public actual fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
    receiver: R,
    completion: Continuation<T>
): Continuation<Unit> {
    val probeCompletion = probeCoroutineCreated(completion)
    return if (this is BaseContinuationImpl)
        create(receiver, probeCompletion)
    else {
        createCoroutineFromSuspendFunction(probeCompletion) {
            (this asFunction2<R, Continuation<T>, Any? >).invoke(receiver, it)/ / 1}}}...private inline fun <T> createCoroutineFromSuspendFunction(
    completion: Continuation<T>,
    crossinline block: (Continuation<T- > >)Any?).: Continuation<Unit> {
    val context = completion.context
    // label == 0 when coroutine is not started yet (initially) or label == 1 when it was
    return if (context === EmptyCoroutineContext)
        object : RestrictedContinuationImpl(completion asContinuation<Any? >) {private var label = 0

            override fun invokeSuspend(result: Result<Any? >): Any? =
                when (label) {
                    0 -> {
                        label = 1
                        result.getOrThrow() // Rethrow exception if trying to start with exception (will be caught by BaseContinuationImpl.resumeWith
                        block(this) // run the block, may return or suspend
                    }
                    1 -> {
                        label = 2
                        result.getOrThrow() // this is the result if the block had suspended
                    }
                    else -> error("This coroutine had already completed")}}else
        object : ContinuationImpl(completion asContinuation<Any? >, context) {private var label = 0

            override fun invokeSuspend(result: Result<Any? >): Any? =
                when (label) {
                    0 -> {
                        label = 1
                        result.getOrThrow() // Rethrow exception if trying to start with exception (will be caught by BaseContinuationImpl.resumeWith
                        block(this) // run the block, may return or suspend
                    }
                    1 -> {
                        label = 2
                        result.getOrThrow() // this is the result if the block had suspended
                    }
                    else -> error("This coroutine had already completed")}}}Copy the code

There is no research association, source code, only take association, source code and see createCoroutineUnintercepted (receiver, completion) method, the method in kotlin source code is modified by suspend key word extension methods, (This as Function2

, Any? >)(this is the (suspend R.() -> T) object, which is the code block decorated by the suspend keyword) converts the suspend keyword to a Function2

, Any? The suspend R.() -> T object is actually compiled into a Function2

, Any? > interface object, and the keyword suspend actually compiles into a Continuation interface
,>
,>
,>

Here’s an example

Case 2

class SuspendTest {

    fun test(a){
        GlobalScope.launch(Dispatchers.Main) {
            Log.d("LogUtils"."-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --")}}}Copy the code

By decompiling example 2 code

public final class SuspendTest {
  public final void test(a) {
    BuildersKt.launch$default((CoroutineScope)GlobalScope.INSTANCE, (CoroutineContext)Dispatchers.getMain(), null.new SuspendTest$test$1(null), 2.null);
  }
  
  
  static final class SuspendTest$testThe $1extends SuspendLambda implements Function2<CoroutineScope.Continuation<? super Unit>, Object> {
    int label;
    
    private CoroutineScope p$;
    
    SuspendTest$test$1(Continuation param1Continuation) {
      super(2, param1Continuation);
    }
    
    public final Continuation<Unit> create(Object param1Object, Continuation
        param1Continuation) {
      Intrinsics.checkParameterIsNotNull(param1Continuation, "completion");
      SuspendTest$test$1 suspendTest$test$1 = new SuspendTest$test$1(param1Continuation);
      CoroutineScope coroutineScope = (CoroutineScope)param1Object;
      suspendTest$test$1.p$ = (CoroutineScope)param1Object;
      return (Continuation<Unit>)suspendTest$test$1;
    }
    
    public final Object invoke(Object param1Object1, Object param1Object2) {
      return ((SuspendTest$test$1)create(param1Object1, (Continuation)param1Object2)).invokeSuspend(Unit.INSTANCE);
    }
    
    public final Object invokeSuspend(Object param1Object) {
      IntrinsicsKt.getCOROUTINE_SUSPENDED();
      if (this.label == 0) {
        ResultKt.throwOnFailure(param1Object);
        param1Object = this.p$;
        Log.d("LogUtils"."-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");
        return Unit.INSTANCE;
      } 
      throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine"); }}}Copy the code

See the Function2 interface

public interface Function2<P1.P2.R> extends Function<R> {
  R invoke(P1 paramP1, P2 paramP2);
}
Copy the code

Seeing the decomcompiled code, at code //1, the code block decorated with the suspend keyword is converted to the Function2 interface class, and the invoke(P1 paramP1, P2 paramP2) overloaded method is called to finally invoke the code block logic

doubt

Can any code block or function decorated with the suspend keyword suspend code?

Let me give you an example

Example 3

class SuspendTest {

    fun test(a){
        GlobalScope.launch(Dispatchers.Main) {
            Log.d("LogUtils".Launch "start")
            suspendTest()
            Log.d("LogUtils"."End of the launch")}}private suspend fun suspendTest(a) {
        Log.d("LogUtils"."Execute a custom suspend decoration method")}}Copy the code

Execution Result:

D/LogUtils: launch a customsuspendD/LogUtils: launch endCopy the code

By comparing the results of Example 3 and Example 1, the user-defined suspend keyword modification method has no effect of suspending or suspending, that is, the suspend keyword does not have the functions of suspending or suspending code blocks or function methods, which need to be implemented by external logic. The Kotlin coroutine framework has pause, suspend code blocks, or function methods that need to be implemented by external logic

For example: The suspend keyword is like the fishing gear provided by a fishing ground. Obviously, fishing gear is not fish, and you can’t fish without fishing. In the same way, suspend is the kotlin keyword. The coroutine framework is a process to suspend and suspend, switch threads, and so on

conclusion

The suspend keyword is essentially an interface that holds a context reference and has a callback method, and Kotlin officially defines several ways to use the suspend keyword. You can use built-in interfaces and methods to suspend or suspend coroutines, switch threads, and so on.

reference

1, Kotlin coroutine hanging is so magical and difficult to understand? I took the skin off him today

Kotlin notes 17 Coroutine suspend keyword