首页 > 移动开发 > ANDROID > 一篇文章带你了解——Kotlin协程
dependencies {
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.32"
// 协程核心库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
// 协程Android支持库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3"
// 协程Java8支持库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.4.3"
// lifecycle对于协程的扩展封装
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
}fun test1() {
GlobalScope.launch {
val arg1 = sunpendF1()
var arg2 = sunpendF2()
doLog("suspend finish arg1:$arg1 arg2:$arg2 result:${arg1 + arg2}")
}
}
private suspend fun sunpendF1(): Int {
delay(1000)
doLog("suspend fun 1")
return 2
}
private suspend fun sunpendF2(): Int {
delay(1000)
doLog("suspend fun 2")
return 4
}01-07 19:21:49.626 9616-10074/com.yy.yylite.kotlinshare I/suspend: suspend fun 1 01-07 19:21:50.633 9616-10074/com.yy.yylite.kotlinshare I/suspend: suspend fun 2 suspend finish arg1:2 arg2:4 result:6
suspend fun test1() {
KLog.i("test1") { "test1" }
val homeItemInfo = HomeItemInfo()
homeItemInfo.adId = "89"
delay(100)
KLog.i("test1") { "test1-end" }
}public final Object test1(@NotNull Continuation var1) {
Object $continuation;
label28: {
if (var1 instanceof <undefinedtype>) {
$continuation = (<undefinedtype>)var1;
if ((((<undefinedtype>)$continuation).getLabel() & Integer.MIN_VALUE) != 0) {
((<undefinedtype>)$continuation).setLabel(((<undefinedtype>)$continuation).getLabel() - Integer.MIN_VALUE);
break label28;
}
}
$continuation = new CoroutineImpl(var1) {
// $FF: synthetic field
Object data;
// $FF: synthetic field
Throwable exception;
Object L$0;
Object L$1;
@Nullable
public final Object doResume(@Nullable Object data, @Nullable Throwable throwable) {
this.data = data;
this.exception = throwable;
super.label |= Integer.MIN_VALUE;
return SuspendTest.this.test1(this);
}
// $FF: synthetic method
final int getLabel() {
return super.label;
}
// $FF: synthetic method
final void setLabel(int var1) {
super.label = var1;
}
};
}
Object var3 = ((<undefinedtype>)$continuation).data;
Throwable var4 = ((<undefinedtype>)$continuation).exception;
Object var6 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
HomeItemInfo homeItemInfo;
switch(((<undefinedtype>)$continuation).getLabel()) {
case 0:
if (var4 != null) {
throw var4;
}
KLog.INSTANCE.i("test1", (Function0)null.INSTANCE);
homeItemInfo = new HomeItemInfo();
homeItemInfo.adId = "89";
((<undefinedtype>)$continuation).L$0 = this;
((<undefinedtype>)$continuation).L$1 = homeItemInfo;
((<undefinedtype>)$continuation).setLabel(1);
if (DelayKt.delay(100, (Continuation)$continuation) == var6) {
return var6;
}
break;
case 1:
homeItemInfo = (HomeItemInfo)((<undefinedtype>)$continuation).L$1;
SuspendTest var7 = (SuspendTest)((<undefinedtype>)$continuation).L$0;
if (var4 != null) {
throw var4;
}
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
KLog.INSTANCE.i("test1", (Function0)null.INSTANCE);
return Unit.INSTANCE;
}public interface Continuation<in T> {
/**
* Context of the coroutine that corresponds to this continuation.
*/
// todo: shall we provide default impl with EmptyCoroutineContext?
public val context: CoroutineContext
/**
* Resumes the execution of the corresponding coroutine passing successful or failed [result] as the
* return value of the last suspension point.
*/
public fun resumeWith(result: Result<T>)
}kotlinx/coroutines/Deferred.delay (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
创建协程
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
/**
* 使用官方库的 MainScope()获取一个协程作用域用于创建协程
*/
private val mScope = MainScope()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 创建一个默认参数的协程,其默认的调度模式为Main 也就是说该协程的线程环境是Main线程
val job1 = mScope.launch {
// 这里就是协程体
// 延迟1000毫秒 delay是一个挂起函数
// 在这1000毫秒内该协程所处的线程不会阻塞
// 协程将线程的执行权交出去,该线程该干嘛干嘛,到时间后会恢复至此继续向下执行
delay(1000)
}
// 创建一个指定了调度模式的协程,该协程的运行线程为IO线程
val job2 = mScope.launch(Dispatchers.IO) {
// 此处是IO线程模式
// 切线程 将协程所处的线程环境切至指定的调度模式Main
withContext(Dispatchers.Main) {
// 现在这里就是Main线程了 可以在此进行UI操作了
}
}
// 下面直接看一个例子: 从网络中获取数据 并更新UI
// 该例子不会阻塞主线程
mScope.launch(Dispatchers.IO) {
// 执行getUserInfo方法时会将线程切至IO去执行
val userInfo = getUserInfo()
// 获取完数据后 切至Main线程进行更新UI
withContext(Dispatchers.Main) {
// 更新UI
}
}
}
/**
* 获取用户信息 该函数模拟IO获取数据
* @return String
*/
private suspend fun getUserInfo(): String {
return withContext(Dispatchers.IO) {
delay(2000)
"Kotlin"
}
}
override fun onDestroy() {
super.onDestroy()
// 取消协程 防止协程泄漏 如果使用lifecycleScope则不需要手动取消
mScope.cancel()
}
}fun asyncTest() {
mScope.launch {
// 开启一个IO模式的线程 并返回一个Deferred,Deferred可以用来获取返回值
// 代码执行到此处时会新开一个协程 然后去执行协程体 父协程的代码会接着往下走
val deferred = async(Dispatchers.IO) {
// 模拟耗时
delay(2000)
// 返回一个值
"Quyunshuo"
}
// 等待async执行完成获取返回值 此处并不会阻塞线程 而是挂起 将线程的执行权交出去
// 等到async的协程体执行完毕后 会恢复协程继续往下执行
val date = deferred.await()
}
}fun asyncTest2() {
mScope.launch {
// 此处有一个需求 同时请求5个接口 并且将返回值拼接起来
val job1 = async {
// 请求1
delay(5000)
"1"
}
val job2 = async {
// 请求2
delay(5000)
"2"
}
val job3 = async {
// 请求3
delay(5000)
"3"
}
val job4 = async {
// 请求4
delay(5000)
"4"
}
val job5 = async {
// 请求5
delay(5000)
"5"
}
// 代码执行到此处时 5个请求已经同时在执行了
// 等待各job执行完 将结果合并
Log.d(
"TAG",
"asyncTest2: ${job1.await()} ${job2.await()} ${job3.await()} ${job4.await()} ${job5.await()}"
)
// 因为我们设置的模拟时间都是5000毫秒 所以当job1执行完时 其他job也均执行完成
}
}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
}fun main() {
val coroutineContext = Job() + Dispatchers.Default + CoroutineName("myContext")
println("$coroutineContext,${coroutineContext[CoroutineName]}")
val newCoroutineContext = coroutineContext.minusKey(CoroutineName)
println("$newCoroutineContext")
}[JobImpl{Active}@7eda2dbb, CoroutineName(myContext), Dispatchers.Default],CoroutineName(myContext)
[JobImpl{Active}@7eda2dbb, Dispatchers.Default]public interface CoroutineContext {
public operator fun <E : Element> get(key: Key<E>): E?
public fun <R> fold(initial: R, operation: (R, Element) -> R): R
public operator fun plus(context: CoroutineContext): CoroutineContext{...}
public fun minusKey(key: Key<*>): CoroutineContext
public interface Key<E : Element>
public interface Element : CoroutineContext {...}
}| State | [isActive] | [isCompleted] | [isCancelled] |
|---|---|---|---|
| New (optional initial state) |
false |
false |
false |
| Active (default initial state) |
true |
false |
false |
| Completing (transient state) |
true |
false |
false |
| Cancelling (transient state) |
false |
false |
true |
| Cancelled (final state) |
false |
true |
true |
| Completed (final state) |
false |
true |
false |
wait children +-----+ start +--------+ complete +-------------+ finish +-----------+ | New | -----> | Active | ---------> | Completing | -------> | Completed | +-----+ +--------+ +-------------+ +-----------+ | cancel / fail | | +----------------+ | | V V +------------+ finish +-----------+ | Cancelling | --------------------------------> | Cancelled | +------------+ +-----------+
public interface Deferred<out T> : Job {
public val onAwait: SelectClause1<T>
public suspend fun await(): T
@ExperimentalCoroutinesApi
public fun getCompleted(): T
@ExperimentalCoroutinesApi
public fun getCompletionExceptionOrNull(): Throwable?
}/**
* Creates a _supervisor_ job object in an active state.
* Children of a supervisor job can fail independently of each other.
*
* A failure or cancellation of a child does not cause the supervisor job to fail and does not affect its other children,
* so a supervisor can implement a custom policy for handling failures of its children:
*
* * A failure of a child job that was created using [launch][CoroutineScope.launch] can be handled via [CoroutineExceptionHandler] in the context.
* * A failure of a child job that was created using [async][CoroutineScope.async] can be handled via [Deferred.await] on the resulting deferred value.
*
* If [parent] job is specified, then this supervisor job becomes a child job of its parent and is cancelled when its
* parent fails or is cancelled. All this supervisor's children are cancelled in this case, too. The invocation of
* [cancel][Job.cancel] with exception (other than [CancellationException]) on this supervisor job also cancels parent.
*
* @param parent an optional parent job.
*/
@Suppress("FunctionName")
public fun SupervisorJob(parent: Job? = null) : CompletableJob = SupervisorJobImpl(parent)/**
* Creates the main [CoroutineScope] for UI components.
*
* Example of use:
* ```
* class MyAndroidActivity {
* private val scope = MainScope()
*
* override fun onDestroy() {
* super.onDestroy()
* scope.cancel()
* }
* }
* ```
*
* The resulting scope has [SupervisorJob] and [Dispatchers.Main] context elements.
* If you want to append additional elements to the main scope, use [CoroutineScope.plus] operator:
* `val scope = MainScope() + CoroutineName("MyActivity")`.
*/
@Suppress("FunctionName")
public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)public interface CoroutineScope {
public val coroutineContext: CoroutineContext
}public object GlobalScope : CoroutineScope {
/**
* Returns [EmptyCoroutineContext].
*/
override val coroutineContext: CoroutineContext
get() = EmptyCoroutineContext
}/**
* Runs a new coroutine and **blocks** the current thread _interruptibly_ until its completion.
* This function should not be used from a coroutine. It is designed to bridge regular blocking code
* to libraries that are written in suspending style, to be used in `main` functions and in tests.
*
* The default [CoroutineDispatcher] for this builder is an internal implementation of event loop that processes continuations
* in this blocked thread until the completion of this coroutine.
* See [CoroutineDispatcher] for the other implementations that are provided by `kotlinx.coroutines`.
*
* When [CoroutineDispatcher] is explicitly specified in the [context], then the new coroutine runs in the context of
* the specified dispatcher while the current thread is blocked. If the specified dispatcher is an event loop of another `runBlocking`,
* then this invocation uses the outer event loop.
*
* If this blocked thread is interrupted (see [Thread.interrupt]), then the coroutine job is cancelled and
* this `runBlocking` invocation throws [InterruptedException].
*
* See [newCoroutineContext][CoroutineScope.newCoroutineContext] for a description of debugging facilities that are available
* for a newly created coroutine.
*
* @param context the context of the coroutine. The default value is an event loop on the current thread.
* @param block the coroutine code.
*/
@Throws(InterruptedException::class)
public fun <T> runBlocking(context: CoroutineContext = EmptyCoroutineContext, block: suspend CoroutineScope.() -> T): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
val currentThread = Thread.currentThread()
val contextInterceptor = context[ContinuationInterceptor]
val eventLoop: EventLoop?
val newContext: CoroutineContext
if (contextInterceptor == null) {
// create or use private event loop if no dispatcher is specified
eventLoop = ThreadLocalEventLoop.eventLoop
newContext = GlobalScope.newCoroutineContext(context + eventLoop)
} else {
// See if context's interceptor is an event loop that we shall use (to support TestContext)
// or take an existing thread-local event loop if present to avoid blocking it (but don't create one)
eventLoop = (contextInterceptor as? EventLoop)?.takeIf { it.shouldBeProcessedFromContext() }
?: ThreadLocalEventLoop.currentOrNull()
newContext = GlobalScope.newCoroutineContext(context)
}
val coroutine = BlockingCoroutine<T>(newContext, currentThread, eventLoop)
coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
return coroutine.joinBlocking()
}/**
* Creates the main [CoroutineScope] for UI components.
*
* Example of use:
* ```
* class MyAndroidActivity {
* private val scope = MainScope()
*
* override fun onDestroy() {
* super.onDestroy()
* scope.cancel()
* }
* }
* ```
*
* The resulting scope has [SupervisorJob] and [Dispatchers.Main] context elements.
* If you want to append additional elements to the main scope, use [CoroutineScope.plus] operator:
* `val scope = MainScope() + CoroutineName("MyActivity")`.
*/
@Suppress("FunctionName")
public fun MainScope(): CoroutineScope = ContextScope(SupervisorJob() + Dispatchers.Main)/** * [CoroutineScope] tied to this [LifecycleOwner]'s [Lifecycle]. * * This scope will be cancelled when the [Lifecycle] is destroyed. * * This scope is bound to * [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]. */ val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope get() = lifecycle.coroutineScope
/**
* [CoroutineScope] tied to this [ViewModel].
* This scope will be canceled when ViewModel will be cleared, i.e [ViewModel.onCleared] is called
*
* This scope is bound to
* [Dispatchers.Main.immediate][kotlinx.coroutines.MainCoroutineDispatcher.immediate]
*/
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))
}/**
* Creates a [CoroutineScope] with [SupervisorJob] and calls the specified suspend block with this scope.
* The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, but overrides
* context's [Job] with [SupervisorJob].
*
* A failure of a child does not cause this scope to fail and does not affect its other children,
* so a custom policy for handling failures of its children can be implemented. See [SupervisorJob] for details.
* A failure of the scope itself (exception thrown in the [block] or cancellation) fails the scope with all its children,
* but does not cancel parent job.
*/
public suspend fun <R> supervisorScope(block: suspend CoroutineScope.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn { uCont ->
val coroutine = SupervisorCoroutine(uCont.context, uCont)
coroutine.startUndispatchedOrReturn(coroutine, block)
}
}
/**
* Creates a [CoroutineScope] and calls the specified suspend block with this scope.
* The provided scope inherits its [coroutineContext][CoroutineScope.coroutineContext] from the outer scope, but overrides
* the context's [Job].
*
* This function is designed for _parallel decomposition_ of work. When any child coroutine in this scope fails,
* this scope fails and all the rest of the children are cancelled (for a different behavior see [supervisorScope]).
* This function returns as soon as the given block and all its children coroutines are completed.
* A usage example of a scope looks like this:
*
* ```
* suspend fun showSomeData() = coroutineScope {
* val data = async(Dispatchers.IO) { // <- extension on current scope
* ... load some UI data for the Main thread ...
* }
*
* withContext(Dispatchers.Main) {
* doSomeWork()
* val result = data.await()
* display(result)
* }
* }
* ```
*
* The scope in this example has the following semantics:
* 1) `showSomeData` returns as soon as the data is loaded and displayed in the UI.
* 2) If `doSomeWork` throws an exception, then the `async` task is cancelled and `showSomeData` rethrows that exception.
* 3) If the outer scope of `showSomeData` is cancelled, both started `async` and `withContext` blocks are cancelled.
* 4) If the `async` block fails, `withContext` will be cancelled.
*
* The method may throw a [CancellationException] if the current job was cancelled externally
* or may throw a corresponding unhandled [Throwable] if there is any unhandled exception in this scope
* (for example, from a crashed coroutine that was started with [launch][CoroutineScope.launch] in this scope).
*/
public suspend fun <R> coroutineScope(block: suspend CoroutineScope.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn { uCont ->
val coroutine = ScopeCoroutine(uCont.context, uCont)
coroutine.startUndispatchedOrReturn(coroutine, block)
}
}import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
/**
* 使用官方库的 MainScope()获取一个协程作用域用于创建协程
*/
private val mScope = MainScope()
companion object {
const val TAG = "Kotlin Coroutine"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mScope.launch(Dispatchers.Default) {
delay(500)
Log.e(TAG, "Child 1")
}
mScope.launch(Dispatchers.Default) {
delay(1000)
Log.e(TAG, "Child 2")
throw RuntimeException("--> RuntimeException <--")
}
mScope.launch(Dispatchers.Default) {
delay(1500)
Log.e(TAG, "Child 3")
}
}
}
打印结果:
E/Kotlin Coroutine: Child 1
E/Kotlin Coroutine: Child 2
E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-3
Process: com.quyunshuo.kotlincoroutine, PID: 24240
java.lang.RuntimeException: --> RuntimeException <--
at com.quyunshuo.kotlincoroutine.MainActivity$onCreate$2.invokeSuspend(MainActivity.kt:31)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
E/Kotlin Coroutine: Child 3override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mScope.launch(Dispatchers.Default) {
delay(500)
Log.e(TAG, "Child 1")
}
// 在Child 2的上下文添加了异常处理
mScope.launch(Dispatchers.Default + CoroutineExceptionHandler { coroutineContext, throwable ->
Log.e(TAG, "CoroutineExceptionHandler: $throwable")
}) {
delay(1000)
Log.e(TAG, "Child 2")
throw RuntimeException("--> RuntimeException <--")
}
mScope.launch(Dispatchers.Default) {
delay(1500)
Log.e(TAG, "Child 3")
}
}
输出结果:
E/Kotlin Coroutine: Child 1
E/Kotlin Coroutine: Child 2
E/Kotlin Coroutine: CoroutineExceptionHandler: java.lang.RuntimeException: --> RuntimeException <--
E/Kotlin Coroutine: Child 3import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
companion object {
const val TAG = "Kotlin Coroutine"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val scope = CoroutineScope(Job() + Dispatchers.Default)
scope.launch(CoroutineExceptionHandler { coroutineContext, throwable ->
Log.e(TAG, "CoroutineExceptionHandler: $throwable")
}) {
supervisorScope {
launch {
delay(500)
Log.e(TAG, "Child 1 ")
}
launch {
delay(1000)
Log.e(TAG, "Child 2 ")
throw RuntimeException("--> RuntimeException <--")
}
launch {
delay(1500)
Log.e(TAG, "Child 3 ")
}
}
}
}
}
输出结果:
E/Kotlin Coroutine: Child 1
E/Kotlin Coroutine: Child 2
E/Kotlin Coroutine: CoroutineExceptionHandler: java.lang.RuntimeException: --> RuntimeException <--
E/Kotlin Coroutine: Child 3
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69983917/viewspace-2839679/ ,如需转载,请注明出处,否则将追究法律责任。