安卓悬浮窗怎么盖在状态栏_引擎盖下的悬浮修饰符

安卓悬浮窗怎么盖在状态栏

Kotlin coroutines introduced the suspend modifier in our daily life as Android developers. Are you curious to see what’s happening under the hood? How is the compiler transforming the code to be able to suspend and resume the execution of coroutines?

Kotlin协程在我们作为Android开发人员的日常生活中引入了suspended修饰符 。 您是否想知道引擎盖下发生了什么? 编译器如何转换代码以暂停和恢复协程的执行?

Knowing this will help you better understand why a suspend function won’t return until all the work that it started has completed and how the code can suspend without blocking threads.

知道这一点将帮助您更好地理解为什么挂起函数要等到它开始的所有工作完成后才返回,以及如何在不阻塞线程的情况下挂起代码。

TL;DR; The Kotlin compiler will create a state machine for every suspend function that manages the coroutine’s execution for us!

TL; DR; Kotlin编译器将为每个挂起函数创建一个状态机,为我们管理协程的执行!

📚 New to coroutines on Android? Check out these Coroutines codelabs:

cor Android上的协程新手? 查看以下协程代码实验室:

If you prefer to watch a video about this, check this out:

如果您想观看有关此的视频,请查看以下内容:

协程101 (Coroutines 101)

Coroutines simplify asynchronous operations on Android. As explained in the documentation, we can use them to manage asynchronous tasks that might otherwise block the main thread and cause your app to freeze.

协程简化了Android上的异步操作。 如文档中所述,我们可以使用它们来管理异步任务,这些任务否则可能会阻塞主线程并导致您的应用程序冻结。

Coroutines are also helpful to replace callback-based APIs with imperative looking code. As example, check out this asynchronous code that uses callbacks:

协程还有助于用命令式外观代码替换基于回调的API。 例如,查看使用回调的异步代码:

// Simplified code that only considers the happy path
fun loginUser(userId: String, password: String, userResult: Callback<User>) {
// Async callbacks
userRemoteDataSource.logUserIn { user ->
// Successful network request
userLocalDataSource.logUserIn(user) { userDb ->
// Result saved in DB
userResult.success(userDb)
}
}
}

Those callbacks can be converted to sequential function calls using coroutines:

可以使用协程将这些回调转换为顺序函数调用:

suspend fun loginUser(userId: String, password: String): User {
val user = userRemoteDataSource.logUserIn(userId, password)
val userDb = userLocalDataSource.logUserIn(user)
return userDb
}

In the coroutines code, we added the suspend modifier to the function. That tells the compiler that this function needs to be executed inside a coroutine. As a developer, you can think of a suspend function as a regular function whose execution might be suspended and resumed at some point.

在协程代码中,我们向函数添加了suspend修饰符。 这告诉编译器该函数需要在协程内部执行。 作为开发人员,您可以将suspend函数视为常规函数,其执行可能会在某些时候被挂起并恢复。

Unlike callbacks, coroutines provide an easy way to swap between threads and handle exceptions.

与回调不同,协程提供了一种在线程之间交换和处理异常的简便方法。

But, what’s the compiler actually doing under the hood when we mark the function as suspend?

但是,当我们将函数标记为suspend时,编译器实际上是在做什么呢?

挂在引擎盖下 (Suspend under the hood)

Back to the loginUser suspend function, notice that the other functions it calls are also suspend functions:

返回loginUser暂停函数,请注意它调用的其他函数也是暂停函数:

suspend fun loginUser(userId: String, password: String): User {
val user = userRemoteDataSource.logUserIn(userId, password)
val userDb = userLocalDataSource.logUserIn(user)
return userDb
}// UserRemoteDataSource.ktsuspend fun logUserIn(userId: String, password: String): User// UserLocalDataSource.ktsuspend fun logUserIn(userId: String): UserDb

In a nutshell, the Kotlin compiler will take suspend functions and convert them to an optimised version of callbacks using a finite state machine (which we’ll cover later).

简而言之,Kotlin编译器将使用暂挂函数,并使用有限状态机将它们转换为优化的回调版本(稍后将介绍)。

You got it right, the compiler will write those callbacks for you!

没错, 编译器将为您编写这些回调

延续界面 (Continuation interface)

The way suspend functions communicate with each other is with Continuation objects. A Continuation is just a generic callback interface with some extra information. As we will see later, it will represent the generated state machine of a suspend function.

挂起函数相互通信的方式是使用Continuation对象。 Continuation只是具有一些额外信息的通用回调接口。 稍后我们将看到,它将代表暂停功能的生成状态机。

Let’s take a look at its definition:

让我们看一下它的定义:

interface Continuation<in T> {
public val context: CoroutineContext
public fun resumeWith(value: Result<T>)
}
  • context will be the CoroutineContext to be used in that continuation.

    context将是在该延续中使用的CoroutineContext

  • resumeWith resumes execution of the coroutine with a Result, that can contain either a value which is the result of the computation that caused the suspension or an exception.

    resumeWith恢复与一个协程的执行Result ,其可以包含一个值,它是引起该悬挂或异常的计算的结果。

Note: From Kotlin 1.3 onwards, you can also use the extension functions resume(value: T) and resumeWithException(exception: Throwable) which are specialised versions of the resumeWith call.

注意:从Kotlin 1.3开始,您还可以使用扩展函数resume (value: T)resumeWithException (exception: Throwable) ,它们是resumeWith调用的专用版本。

The compiler will replace the suspend modifier with the extra parameter completion (of type Continuation) in the function signature that will be used to communicate the result of the suspend function to the coroutine that called it:

编译器将在函数签名中用额外的参数completion (类型Continuation )替换suspend修饰符,该参数将用于将suspend函数的结果传递给调用它的协程:

fun loginUser(userId: String, password: String, completion: Continuation<Any?>) {
val user = userRemoteDataSource.logUserIn(userId, password)
val userDb = userLocalDataSource.logUserIn(user)completion.resume(userDb)
}

For simplicity, our example will return Unit instead of User. The User object will be “returned” in the added Continuation parameter.

为简单起见,我们的示例将返回Unit而不是User 。 在添加的Continuation参数中将“返回” User对象。

The bytecode of suspend functions actually return Any? because it's a union type of T | COROUTINE_SUSPENDED. That allows the function to return synchronously when it can.

暂停函数的字节码实际上返回Any? 因为它是T | COROUTINE_SUSPENDED的并集类型T | COROUTINE_SUSPENDED T | COROUTINE_SUSPENDED 。 这允许函数在可能的情况下同步返回。

Note: If you mark a function that doesn’t call other suspend functions with the suspend modifier, the compiler will add the extra Continuation parameter but won’t do anything with it, the function body’s bytecode will look like a regular function.

注意 :如果使用suspend修饰符标记了一个不调用其他暂停函数的函数,则编译器将添加额外的Continuation参数,但不会对其执行任何操作,该函数体的字节码将看起来像常规函数。

You can also see the Continuation interface in other places:

您还可以在其他地方看到Continuation界面:

  • When converting callback-based APIs to coroutines using suspendCoroutine or suspendCancellableCoroutine (which you should always prefer), you directly interact with a Continuation object to resume the coroutine that got suspended after running the block of code passed as a parameter.

    在使用suspendCoroutinesuspendCancellableCoroutine (您应该始终首选)将基于回调的API转换为协程时,您可以直接与Continuation对象进行交互,以恢复在运行作为参数传递的代码块后被暂停的协程。

  • You can start a coroutine with the startCoroutine extension function on a suspend function. It takes a Continuation object as a parameter that will get called when the new coroutine finishes with either a result or an exception.

    您可以在暂停功能上使用startCoroutine扩展功能启动协程。 它以Continuation对象作为参数,当新协程以结果或异常结束时将被调用。

使用不同的调度程序 (Using different Dispatchers)

You can swap between different Dispatchers to execute computations on different threads. How does Kotlin know where to resume a suspended computation?

您可以在不同的分派器之间交换以在不同的线程上执行计算。 Kotlin如何知道在哪里恢复暂停的计算?

There’s a subtype of Continuation called DispatchedContinuation whose resume function makes a dispatch call to the Dispatcher available in the CoroutineContext. All Dispatchers will call dispatch except Dispatchers.Unconfined whose isDispatchNeeded function override (that is called before dispatch) always returns false.

有一个Continuation的子类型,称为DispatchedContinuation其恢复函数可在CoroutineContext提供对Dispatcher的调度调用。 所有调度程序将调用调度除了Dispatchers.UnconfinedisDispatchNeeded功能覆盖(即之前称为dispatch )总是返回false

生成状态机 (The generated State machine)

Disclaimer: The code to be shown in the rest of the article will not fully match the bytecode generated by the compiler. It will be Kotlin code accurate enough to allow you to understand what’s really happening internally. This representation is generated by Coroutines version 1.3.3 and might change in future versions of the library.

免责声明 :本文其余部分中显示的代码将与编译器生成的字节码不完全匹配。 Kotlin代码将足够准确,以使您了解内部真正发生的事情。 此表示形式是由Coroutines版本1.3.3生成的,并且可能在库的将来版本中更改。

The Kotlin compiler will identify when the function can suspend internally. Every suspension point will be represented as a state in the finite state machine. These states are represented with labels by the compiler:

Kotlin编译器将确定该函数何时可以在内部挂起。 每个悬浮点将在有限状态机中表示为一个状态。 这些状态由编译器用标签表示:

fun loginUser(userId: String, password: String, completion: Continuation<Any?>) {
// Label 0 -> first execution
val user = userRemoteDataSource.logUserIn(userId, password) // Label 1 -> resumes from userRemoteDataSource
val userDb = userLocalDataSource.logUserIn(user) // Label 2 -> resumes from userLocalDataSource
completion.resume(userDb)
}

For a better representation of the state machine, the compiler will use a when statement to implement the different states:

为了更好地表示状态机,编译器将使用when语句来实现不同的状态:

fun loginUser(userId: String, password: String, completion: Continuation<Any?>) {
when(label) {0 -> { // Label 0 -> first execution
userRemoteDataSource.logUserIn(userId, password)
} 1 -> { // Label 1 -> resumes from userRemoteDataSource
userLocalDataSource.logUserIn(user)
} 2 -> { // Label 2 -> resumes from userLocalDataSource
completion.resume(userDb)
}
else -> throw IllegalStateException(...)
}
}

This code is incomplete since the different states have no way to share information. The compiler will use the same Continuation object in the function to do it. This is why the generic of the Continuation is Any? instead of the return type of the original function (i.e. User).

该代码不完整,因为不同的州无法共享信息。 编译器将在函数中使用相同的Continuation对象。 这就是为什么Continuation的泛型是Any? 而不是原始函数的返回类型(即User )。

Furthermore, the compiler will create a private class that 1) holds the required data and 2) calls the loginUser function recursively to resume execution. You can check out an approximation of the generated class below.

此外,编译器将创建一个私有类,该私有类1)保存所需的数据,2)递归调用loginUser函数以恢复执行。 您可以在下面查看生成的类的近似值。

Disclaimer: Comments are not generated by the compiler. I added them to explain what they do and make following the code easier to follow.

免责声明 :注释不是由编译器生成的。 我添加了它们以解释它们的作用,并使遵循代码更容易遵循。

fun loginUser(userId: String?, password: String?, completion: Continuation<Any?>) {  class LoginUserStateMachine(
// completion parameter is the callback to the function
// that called loginUser
completion: Continuation<Any?>
): CoroutineImpl(completion) { // Local variables of the suspend function
var user: User? = null
var userDb: UserDb? = null // Common objects for all CoroutineImpls
var result: Any? = null
var label: Int = 0 // this function calls the loginUser again to trigger the
// state machine (label will be already in the next state) and
// result will be the result of the previous state's computation
override fun invokeSuspend(result: Any?) {
this.result = resultloginUser(null, null, this)
}
}
...
}

As invokeSuspend will call loginUser again with just the information of the Continuation object, the rest of parameters in the loginUser function signature become nullable. At this point, the compiler just needs to add information on how to move between states.

由于invokeSuspend仅使用Continuation对象的信息再次调用loginUserloginUser函数签名中的其余参数将变为可空。 此时,编译器只需要添加有关如何在状态之间移动的信息。

The first thing it needs to do is know if 1) it’s the first time the function is called or 2) the function has resumed from a previous state. It does it by checking if the continuation passed in is of type LoginUserStateMachine or not:

它需要做的第一件事是知道1)是第一次调用该函数还是2)该函数已从先前的状态恢复。 它通过检查传入的连续性是否为LoginUserStateMachine类型来做到这一点:

fun loginUser(userId: String?, password: String?, completion: Continuation<Any?>) {
... val continuation = completion as? LoginUserStateMachine ?: LoginUserStateMachine(completion) ...
}

If it’s the first time, it will create a new LoginUserStateMachine instance and will store the completion instance received as a parameter so that it remembers how to resume the function that called this one. If it’s not, it will just carry on executing the state machine (the suspend function).

如果是第一次,它将创建一个新的LoginUserStateMachine实例,并将接收到的completion实例存储为参数,以便它记住如何恢复调用此函数的函数。 如果不是,它将继续执行状态机(暂停功能)。

Now, let’s see the code that the compiler generates for moving between states and sharing information between them.

现在,让我们看看编译器生成的用于在状态之间移动和在状态之间共享信息的代码。

/* Copyright 2019 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 */
fun loginUser(userId: String?, password: String?, completion: Continuation<Any?>) {
    ...


    val continuation = completion as? LoginUserStateMachine ?: LoginUserStateMachine(completion)


    when(continuation.label) {
        0 -> {
            // Checks for failures
            throwOnFailure(continuation.result)
            // Next time this continuation is called, it should go to state 1
            continuation.label = 1
            // The continuation object is passed to logUserIn to resume 
            // this state machine's execution when it finishes
            userRemoteDataSource.logUserIn(userId!!, password!!, continuation)
        }
        1 -> {
            // Checks for failures
            throwOnFailure(continuation.result)
            // Gets the result of the previous state
            continuation.user = continuation.result as User
            // Next time this continuation is called, it should go to state 2
            continuation.label = 2
            // The continuation object is passed to logUserIn to resume 
            // this state machine's execution when it finishes
            userLocalDataSource.logUserIn(continuation.user, continuation)
        }
        ... // leaving out the last state on purpose
    }
}

Spend some time going through the code above and see if you can spot the differences with the previous snippets of code. Let’s see what the compiler generates:

花一些时间浏览上面的代码,看看是否可以发现与以前的代码片段之间的差异。 让我们看看编译器生成的内容:

  • The when statement’s argument is the label from inside the LoginUserStateMachine instance.

    when语句的参数是LoginUserStateMachine实例内部的label

  • Every time a new state is processed, there’s a check in case a failure happened when this function was suspended.

    每次处理新状态时,都会进行检查,以防该功能挂起时发生故障。
  • Before calling the next suspend function (i.e. logUserIn), the label of the LoginUserStateMachine instance is updated to the next state.

    在调用下一个挂起函数(即logUserIn )之前, LoginUserStateMachine实例的label将更新为下一个状态。

  • When inside this state machine there’s a call to another suspend function, the instance of the continuation (of type LoginUserStateMachine) is passed as a parameter. The suspend function to be called has also been transformed by the compiler and it’s another state machine like this one that takes a continuation object as a parameter! When the state machine of that suspend function finishes, it will resume the execution of this state machine.

    当在此状态机内调用另一个暂挂函数时, continuation实例(类型LoginUserStateMachine )作为参数传递。 待调用的suspend函数也已由编译器进行了转换,这是另一个这样的状态机,它以延续对象作为参数! 当该暂挂功能的状态机完成时,它将恢复该状态机的执行。

The last state is different since it has to resume the execution of the function that called this one, as you can see in the code, it calls resume on the cont variable stored (at construction time) in LoginUserStateMachine:

最后一个状态是不同的,因为它必须恢复调用该函数的函数的执行,如您在代码中所见,它对在LoginUserStateMachine存储的cont变量(在构造时)调用resume:

/* Copyright 2019 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 */
fun loginUser(userId: String?, password: String?, completion: Continuation<Any?>) {
    ...


    val continuation = completion as? LoginUserStateMachine ?: LoginUserStateMachine(completion)


    when(continuation.label) {
        ...
        2 -> {
            // Checks for failures
            throwOnFailure(continuation.result)
            // Gets the result of the previous state
            continuation.userDb = continuation.result as UserDb
            // Resumes the execution of the function that called this one
            continuation.cont.resume(continuation.userDb)
        }
        else -> throw IllegalStateException(...)
    }
}

As you see, the Kotlin compiler is doing a lot for us! From this suspend function:

如您所见,Kotlin编译器为我们做了很多工作! 通过此暂停功能:

suspend fun loginUser(userId: String, password: String): User {
val user = userRemoteDataSource.logUserIn(userId, password)
val userDb = userLocalDataSource.logUserIn(user)
return userDb
}

The compiler generated all of this for us:

编译器为我们生成了所有这些:

/* Copyright 2019 Google LLC.	
   SPDX-License-Identifier: Apache-2.0 */
fun loginUser(userId: String?, password: String?, completion: Continuation<Any?>) {


    class LoginUserStateMachine(
        // completion parameter is the callback to the function that called loginUser
        completion: Continuation<Any?>
    ): CoroutineImpl(completion) {
        // objects to store across the suspend function
        var user: User? = null
        var userDb: UserDb? = null


        // Common objects for all CoroutineImpl
        var result: Any? = null
        var label: Int = 0


        // this function calls the loginUser again to trigger the 
        // state machine (label will be already in the next state) and 
        // result will be the result of the previous state's computation
        override fun invokeSuspend(result: Any?) {
            this.result = result
            loginUser(null, null, this)
        }
    }


    val continuation = completion as? LoginUserStateMachine ?: LoginUserStateMachine(completion)


    when(continuation.label) {
        0 -> {
            // Checks for failures
            throwOnFailure(continuation.result)
            // Next time this continuation is called, it should go to state 1
            continuation.label = 1
            // The continuation object is passed to logUserIn to resume 
            // this state machine's execution when it finishes
            userRemoteDataSource.logUserIn(userId!!, password!!, continuation)
        }
        1 -> {
            // Checks for failures
            throwOnFailure(continuation.result)
            // Gets the result of the previous state
            continuation.user = continuation.result as User
            // Next time this continuation is called, it should go to state 2
            continuation.label = 2
            // The continuation object is passed to logUserIn to resume 
            // this state machine's execution when it finishes
            userLocalDataSource.logUserIn(continuation.user, continuation)
        }
        2 -> {
            // Checks for failures
            throwOnFailure(continuation.result)
            // Gets the result of the previous state
            continuation.userDb = continuation.result as UserDb
            // Resumes the execution of the function that called this one
            continuation.cont.resume(continuation.userDb)
        }
        else -> throw IllegalStateException(...)
    }
}

The Kotlin compiler transforms every suspend function to be a state machine, which optimises using callbacks every time a function needs to suspend.

Kotlin编译器将每个暂挂函数转换为状态机,从而在每次需要暂停函数时使用回调进行优化。

Knowing what the compiler does under the hood now, you can better understand why a suspend function won’t return until all the work that it started has completed. Also, how the code can suspend without blocking threads: the information of what needs to be executed when the function is resumed is stored in the Continuation object!

了解了编译器的功能后,您可以更好地理解为什么挂起函数在启动所有工作之前都不会返回。 同样,如何在不阻塞线程的情况下暂停代码:恢复功能时需要执行的操作信息存储在Continuation对象中!

翻译自: https://medium.com/androiddevelopers/the-suspend-modifier-under-the-hood-b7ce46af624f

安卓悬浮窗怎么盖在状态栏

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值