android中的协程

参考

https://developer.android.com/kotlin/coroutines

https://www.bennyhuo.com/2019/05/27/coroutine-android/

https://juejin.im/post/6854573211418361864

导入依赖

除了要导入kotlin协程依赖外,还需要导入Android主线程协程库:

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'

Retrofit的支持

https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter

implementation 'com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2'

此项目已经被deprecated了,因为在Retrofit 2.6.0+已经支持kotlin的suspend了。

MainScope

public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)

通过MainScope()方法可以创建一个主线程的CoroutineScope,而主线程的dispatcher在Android中是用handler实现的,那么就可以使用协程的相关方法了,比如launch等。

val mainScope = MainScope()
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    launchButton.setOnClickListener {
        mainScope.launch {
            log(1)
            textView.text = async(Dispatchers.IO) {
                log(2)
                delay(1000)
                log(3)
                "Hello1111"
            }.await()
            log(4)
        }
    }
}


override fun onDestroy() {
    super.onDestroy()
    mainScope.cancel()
}

记得在onDestory中对这个mainScope进行cancel,防止内存泄露。

通过委托来实现

让activity实现CoroutineScope 接口,再by MainScope()

abstract class ScopedActivity: Activity(), CoroutineScope by MainScope(){
    override fun onDestroy() {
        super.onDestroy()
        cancel()
    }
}

将 Kotlin 协程与Architecture组件一起使用

https://developer.android.google.cn/topic/libraries/architecture/coroutines

除了MainScope()外,Android官方还提供了几个Architecture组件的协程/suspend的支持。

lifecycleScope

lifecycle可能是最常用到的一个架构组件,android官方提供了个kotlin协程的支持库

    dependencies {
        implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
    }

然后我们就可以直接在activity/fragment中直接通过lifecycleScope来在合适的生命周期启动协程,并在activity/fragment销毁时也自动取消lifecycleScope中启动的协程。

lifecycleScope是LifecycleCoroutineScope的对象,在LifecycleCoroutineScope定义了几个便捷的方法,让我们在合适的生命周期时机运行代码:

abstract class LifecycleCoroutineScope internal constructor() : CoroutineScope {
    internal abstract val lifecycle: Lifecycle

    /**
     * Launches and runs the given block when the [Lifecycle] controlling this
     * [LifecycleCoroutineScope] is at least in [Lifecycle.State.CREATED] state.
     *
     * The returned [Job] will be cancelled when the [Lifecycle] is destroyed.
     * @see Lifecycle.whenCreated
     * @see Lifecycle.coroutineScope
     */
    fun launchWhenCreated(block: suspend CoroutineScope.() -> Unit): Job = launch {
        lifecycle.whenCreated(block)
    }

    /**
     * Launches and runs the given block when the [Lifecycle] controlling this
     * [LifecycleCoroutineScope] is at least in [Lifecycle.State.STARTED] state.
     *
     * The returned [Job] will be cancelled when the [Lifecycle] is destroyed.
     * @see Lifecycle.whenStarted
     * @see Lifecycle.coroutineScope
     */

    fun launchWhenStarted(block: suspend CoroutineScope.() -> Unit): Job = launch {
        lifecycle.whenStarted(block)
    }

    /**
     * Launches and runs the given block when the [Lifecycle] controlling this
     * [LifecycleCoroutineScope] is at least in [Lifecycle.State.RESUMED] state.
     *
     * The returned [Job] will be cancelled when the [Lifecycle] is destroyed.
     * @see Lifecycle.whenResumed
     * @see Lifecycle.coroutineScope
     */
    fun launchWhenResumed(block: suspend CoroutineScope.() -> Unit): Job = launch {
        lifecycle.whenResumed(block)
    }
}

来跟一下源码

val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() = lifecycle.coroutineScope
val Lifecycle.coroutineScope: LifecycleCoroutineScope
    get() {
        while (true) {
            val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
            if (existing != null) {
                return existing
            }
            val newScope = LifecycleCoroutineScopeImpl(
                this,
                SupervisorJob() + Dispatchers.Main.immediate
            )
            if (mInternalScopeRef.compareAndSet(null, newScope)) {
                newScope.register()
                return newScope
            }
        }
    }

当创建完newScope 后就会调用其register()进行添加生命周期的观察者。

internal class LifecycleCoroutineScopeImpl(
    override val lifecycle: Lifecycle,
    override val coroutineContext: CoroutineContext
) : LifecycleCoroutineScope(), LifecycleEventObserver {
    init {
        // in case we are initialized on a non-main thread, make a best effort check before
        // we return the scope. This is not sync but if developer is launching on a non-main
        // dispatcher, they cannot be 100% sure anyways.
        if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
            coroutineContext.cancel()
        }
    }

    fun register() {
        launch(Dispatchers.Main.immediate) {
            if (lifecycle.currentState >= Lifecycle.State.INITIALIZED) {
                lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)
            } else {
                coroutineContext.cancel()
            }
        }
    }

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
            lifecycle.removeObserver(this)
            coroutineContext.cancel()
        }
    }
}

当生命周期发生变化时会判断当前状态是否已经DESTROYED,是的话就把协程job取消。

扩展函数

除了上边的方法外,还有几个扩展函数能达到上边方法的效果。

suspend fun <T> LifecycleOwner.whenCreated(block: suspend CoroutineScope.() -> T): T =
    lifecycle.whenCreated(block)

/**
 * Runs the given block when the [Lifecycle] is at least in [Lifecycle.State.CREATED] state.
 *
 * @see Lifecycle.whenStateAtLeast for details
 */
suspend fun <T> Lifecycle.whenCreated(block: suspend CoroutineScope.() -> T): T {
    return whenStateAtLeast(Lifecycle.State.CREATED, block)
}

/**
 * Runs the given block when the [LifecycleOwner]'s [Lifecycle] is at least in
 * [Lifecycle.State.STARTED] state.
 *
 * @see Lifecycle.whenStateAtLeast for details
 */
suspend fun <T> LifecycleOwner.whenStarted(block: suspend CoroutineScope.() -> T): T =
    lifecycle.whenStarted(block)

/**
 * Runs the given block when the [Lifecycle] is at least in [Lifecycle.State.STARTED] state.
 *
 * @see Lifecycle.whenStateAtLeast for details
 */
suspend fun <T> Lifecycle.whenStarted(block: suspend CoroutineScope.() -> T): T {
    return whenStateAtLeast(Lifecycle.State.STARTED, block)
}

/**
 * Runs the given block when the [LifecycleOwner]'s [Lifecycle] is at least in
 * [Lifecycle.State.RESUMED] state.
 *
 * @see Lifecycle.whenStateAtLeast for details
 */
suspend fun <T> LifecycleOwner.whenResumed(block: suspend CoroutineScope.() -> T): T =
    lifecycle.whenResumed(block)

/**
 * Runs the given block when the [Lifecycle] is at least in [Lifecycle.State.RESUMED] state.
 *
 * @see Lifecycle.whenStateAtLeast for details
 */
suspend fun <T> Lifecycle.whenResumed(block: suspend CoroutineScope.() -> T): T {
    return whenStateAtLeast(Lifecycle.State.RESUMED, block)
}

上边的方法都调用到了一个方法whenStateAtLeast,看下这个方法的实现:

suspend fun <T> Lifecycle.whenStateAtLeast(
    minState: Lifecycle.State,
    block: suspend CoroutineScope.() -> T
) = withContext(Dispatchers.Main.immediate) {
    val job = coroutineContext[Job] ?: error("when[State] methods should have a parent job")
    val dispatcher = PausingDispatcher()
    val controller =
        LifecycleController(this@whenStateAtLeast, minState, dispatcher.dispatchQueue, job)
    try {
        withContext(dispatcher, block)
    } finally {
        controller.finish()
    }
}
internal class LifecycleController(
    private val lifecycle: Lifecycle,
    private val minState: Lifecycle.State,
    private val dispatchQueue: DispatchQueue,
    parentJob: Job
) {
    private val observer = LifecycleEventObserver { source, _ ->
        if (source.lifecycle.currentState == Lifecycle.State.DESTROYED) {
            // cancel job before resuming remaining coroutines so that they run in cancelled
            // state
            handleDestroy(parentJob)
        } else if (source.lifecycle.currentState < minState) {
            dispatchQueue.pause()
        } else {
            dispatchQueue.resume()
        }
    }

    init {
        // If Lifecycle is already destroyed (e.g. developer leaked the lifecycle), we won't get
        // an event callback so we need to check for it before registering
        // see: b/128749497 for details.
        if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
            handleDestroy(parentJob)
        } else {
            lifecycle.addObserver(observer)
        }
    }

    @Suppress("NOTHING_TO_INLINE") // avoid unnecessary method
    private inline fun handleDestroy(parentJob: Job) {
        parentJob.cancel()
        finish()
    }

    /**
     * Removes the observer and also marks the [DispatchQueue] as finished so that any remaining
     * runnables can be executed.
     */
    @MainThread
    fun finish() {
        lifecycle.removeObserver(observer)
        dispatchQueue.finish()
    }
}

可以看到当执行一次完成后,就直接结束。也就是说当在onResume时执行一段代码,然后onPause再onResume时是不会再执行一次的。

viewModelScope

在viewModel和Activity/fragment的生命周期是不一致的,所以是不能直接使用lifecycleScope的,其实是可以使用MainScope的,只不过要进行手动管理取消。

所以Android官方又贴心的为我们开发了一个viewModel的协程支持库。

    dependencies {
        implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
    }

然后在viewModel中就可以通过viewModelScope调用协程相关方法来启动协程,比如launch。

例如,以下 viewModelScope会启动一个协程,用于在后台线程中发出网络请求。该库会处理所有设置和相应的范围清除:

class MainViewModel : ViewModel() {
    // Make a network request without blocking the UI thread
    private fun makeNetworkRequest() {
        // launch a coroutine in viewModelScope
        viewModelScope.launch  {
            remoteApi.slowFetch()
            ...
        }
    }

    // No need to override onCleared()
}

源码

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 fun close() {
        coroutineContext.cancel()
    }
}

官方说viewModelScope会自动的取消viewModelScope中的协程,那么官方是怎么实现的呢?

还得回到viewModel中:

private final Map<String, Object> mBagOfTags = new HashMap<>();
private volatile boolean mCleared = false;

<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) {
        // It is possible that we'll call close() multiple times on the same object, but
        // Closeable interface requires close method to be idempotent:
        // "if the stream is already closed then invoking this method has no effect." (c)
        closeWithRuntimeException(result);
    }
    return result;
}

@MainThread
final void clear() {
    mCleared = true;
    // Since clear() is final, this method is still called on mock objects
    // and in those cases, mBagOfTags is null. It'll always be empty though
    // because setTagIfAbsent and getTag are not final so we can skip
    // clearing it
    if (mBagOfTags != null) {
        synchronized (mBagOfTags) {
            for (Object value : mBagOfTags.values()) {
                // see comment for the similar call in setTagIfAbsent
                closeWithRuntimeException(value);
            }
        }
    }
    onCleared();
}

protected void onCleared() {
}

private static void closeWithRuntimeException(Object obj) {
    if (obj instanceof Closeable) {
        try {
            ((Closeable) obj).close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

可以看到当viewmodel被clear时,会先把mBagOfTags 这个map中的值一个个的取出来去close(如果值是Closeable的话),

而在viewModelScope的定义处会创建一个CloseableCoroutineScope加入到mBagOfTags 中,而CloseableCoroutineScope是实现了Closeable接口的,

所以当viewmodel被clear时,viewModelScope启动的协程会自动cancel。

将协程与 LiveData 一起使用

这个不常使用。

使用 LiveData 时,您可能需要异步计算值。例如,您可能需要检索用户的偏好设置并将其传送给界面。在这些情况下,LiveData KTX 可提供一个 liveData 构建器函数,该函数会调用 suspend 函数,并将结果作为 LiveData 对象传送。

要使用此模块,请将以下内容添加到应用的 build.gradle 文件中:

    dependencies {
        implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
    }

然后就可以调用liveData函数来异步计算值。

当 LiveData 变为非活动状态onInactive()时,代码块会在可配置的超时过后自动取消。如果代码块在完成前取消,则会在 LiveData 再次变为活动状态onActive()后重启;如果在上次运行中成功完成,则不会重启。请注意,代码块只有在自动取消的情况下才会重启。如果代码块由于任何其他原因(例如,抛出 CancelationException)而取消,则不会重启。

在以下示例中,loadUser() 是在其他地方声明的 suspend 函数。 您可以使用 liveData 构建器函数异步调用 loadUser(),然后使用 emit() 来发出结果:

val user: LiveData<User> = liveData {
    val data = database.loadUser() // loadUser is a suspend function.
    emit(data)
}

源码

fun <T> liveData(
    context: CoroutineContext = EmptyCoroutineContext,
    timeoutInMs: Long = DEFAULT_TIMEOUT,
    @BuilderInference block: suspend LiveDataScope<T>.() -> Unit
): LiveData<T> = CoroutineLiveData(context, timeoutInMs, block)

其中的lambda会有一个接收者是LiveDataScope,

同时可以指定一个timeoutInMs,超过指定时间后

interface LiveDataScope<T> {
    /**
     * Set's the [LiveData]'s value to the given [value]. If you've called [emitSource] previously,
     * calling [emit] will remove that source.
     *
     * Note that this function suspends until the value is set on the [LiveData].
     *
     * @param value The new value for the [LiveData]
     *
     * @see emitSource
     */
    suspend fun emit(value: T)

    /**
     * Add the given [LiveData] as a source, similar to [MediatorLiveData.addSource]. Calling this
     * method will remove any source that was yielded before via [emitSource].
     *
     * @param source The [LiveData] instance whose values will be dispatched from the current
     * [LiveData].
     *
     * @see emit
     * @see MediatorLiveData.addSource
     * @see MediatorLiveData.removeSource
     */
    suspend fun emitSource(source: LiveData<T>): DisposableHandle

    /**
     * References the current value of the [LiveData].
     *
     * If the block never `emit`ed a value, [latestValue] will be `null`. You can use this
     * value to check what was then latest value `emit`ed by your `block` before it got cancelled.
     *
     * Note that if the block called [emitSource], then `latestValue` will be last value
     * dispatched by the `source` [LiveData].
     */
    val latestValue: T?
}
原文地址:https://www.cnblogs.com/muouren/p/13948988.html