}
Log.d(“CoroutineTag”, “hello”)

运行结果如下:

hello
Kotlin Coroutines World!

2.3 GlobalScope.launch源码分析

在2.2的代码中,GlobalScope是CoroutineScope的一个实例;launch是CoroutineScope的扩展函数,用来启动协程,扩展函数定义如下:

/**

  • 启动一个新的协程,并把该协程以Job形式返回;
  • 可通过Job的start()、cancle()、join()等方法控制该协程的执行;
  • 接收三个参数
  • @param context 协程上下文
  • @param start 协程启动方式,默认值是CoroutineStart.DEFAULT
  • @param block 需要被调用的挂起函数
  • @return job 以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
    }

下面分别分析CoroutineScope、CoroutineScope.launch()、CoroutineContext、CoroutineStart来介绍协程机制。

三、CoroutineScope

Coroutine Scope即协程作用域,是协程运行的作用范围;每个异步操作都在特定范围内运行。其定义如下:

/**

  • CoroutineScope定义了协程作用范围;
  • 通过CoroutineScope.launch或CoroutineScope.aync创建一个新的CoroutineScope并启动一个新的协程时,新的CoroutineScope会同时继承外部CoroutineScope的coroutineContext;
    */
    public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
    }

3.1 创建CoroutineScope

使用合适的CoroutineScope,并在合适的时机销毁,避免内存泄漏。例如在Activity销毁时取消CoroutineScope内的协程,避免内存泄漏;
创建CoroutineScope的方式:
(1)CoroutineScope();通用的CoroutineScope();
(2)MainScope();作用域为UI生命周期,默认调度器为Dispatchers.Main;如下所示:

class MyAndroidActivity {
private val scope = MainScope()

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

以下介绍常见的CoroutineScope及会创建CoroutineScope的场景:

3.2 GlobalScope

GlobalScope作用域中的协程在App启动后可一直执行至该协程执行结束或取消,常用来启动一些需要在application生命周期内运行且不能提前取消的顶级协程。
对于一些Activity或Fragment销毁后就不在需要执行的协程,不建议使用GlobalScope来启动。

3.3 CoroutineScope.launch

GlobalScope.launch {
Log.d(“CoroutineTag”, “GlobalScope”)
launch {
// 协程内的this即CoroutineScope
Log.d(“CoroutineTag”, “coroutineScope”)
}
}

通过CoroutineScope.launch的无参启动方式,会创建一个CoroutineScope,这个CoroutineScope继承了外面CoroutineScope的CoroutineContext。

3.4 CoroutineScope.aync

GlobalScope.launch {
Log.d(“CoroutineTag”, “GlobalScope”)
async {
// 协程内的this即CoroutineScope
Log.d(“CoroutineTag”, “coroutineScope”)
}
}

通过CoroutineScope.async的无参启动方式,会创建一个CoroutineScope,这个CoroutineScope继承了外面CoroutineScope的CoroutineContext。
CoroutineScope.async与CoroutineScope.launch的区别在于,async方式返回Deferred,可以获取协程的执行结果。

3.5 coroutineScope

在其他协程或挂起函数内,可通过coroutineScope来创建一个CoroutineScope,并且会立即执行coroutineScope{}内新建的协程,如下所示:

GlobalScope.launch {
Log.d(“CoroutineTag”, “GlobalScope”)
coroutineScope {
// 协程内的this即CoroutineScope
Log.d(“CoroutineTag”, “coroutineScope”)
}
}

coroutineScope是一个挂起挂起函数,所以只能在协程或挂起函数内调用:

public suspend fun 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)
}
}

3.6 runBlocking

在线程、协程、挂起函数内,可通过runBlocking创建一个CoroutineScope,并且会立即执行runBlocking{}内新建的协程,如下所示:

GlobalScope.launch {
Log.d(“CoroutineTag”, “GlobalScope”)
runBlocking {
// 协程内的this即CoroutineScope
Log.d(“CoroutineTag”, “coroutineScope”)
}
}

与coroutineScope不同的是,runBlocking会阻塞当前线程;runBlocking是一个普通方法,所以runBlocking可以在线程中调用:

/**

  • @param block 挂起函数,返回结果T就是协程的返回结果
    */
    public fun 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(newContext, currentThread, eventLoop)
    coroutine.start(CoroutineStart.DEFAULT, coroutine, block)
    return coroutine.joinBlocking()
    }

3.7 MVVM的CoroutineScope

参考:developer.android.com/topic/libra…

对于MVVM架构,KTX提供了LifecycleOwner、LiveData、ViewModel对应的CoroutineScope;使用方法如下:
(1)添加依赖

dependencies {
// ViewModelScope
implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0”
// LifecycleScope
implementation “androidx.lifecycle:lifecycle-runtime-ktx:2.2.0”
// liveData
implementation “androidx.lifecycle:lifecycle-livedata-ktx:2.2.0”
}

(2)用法示例

// 1.viewModelScope
class MyViewModel: ViewModel() {
init {
viewModelScope.launch {
// Coroutine that will be canceled when the ViewModel is cleared.
}
}
}

// 2.lifecycleScope
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
val params = TextViewCompat.getTextMetricsParams(textView)
val precomputedText = withContext(Dispatchers.Default) {
PrecomputedTextCompat.create(longTextContent, params)
}
TextViewCompat.setPrecomputedText(textView, precomputedText)
}
}
}

四、协程启动方式

coroutine builder协程的启动方式有几种:

4.1 CoroutineScope.launch

启动一个协程但不会阻塞调用线程,必须在协程作用域CoroutineScope中才能调用,返回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
}

4.2 CoroutineScope.async

启动一个协程但不会阻塞调用线程,必须在协程作用域CoroutineScope中才能调用。返回Deferred表示该新建的协程。Deferred是Job的子接口。

public fun CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}

CoroutineScope.async与CoroutineScope.launch的区别在于,async方式返回Deferred,可以获取协程的执行结果。

4.3 withContext

指定一个CoroutineContext,并启动一个协程,使用方式如下:

GlobalScope.launch {
withContext(Dispatchers.Default) {
}

withContext是一个挂起函数,只能在其他协程或挂起函数内调用:

public suspend fun withContext(
context: CoroutineContext,
block: suspend CoroutineScope.() -> T
): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
// compute new context
val oldContext = uCont.context
val newContext = oldContext + context
// always check for cancellation of new context
newContext.checkCompletion()
// FAST PATH #1 – new context is the same as the old one
if (newContext === oldContext) {
val coroutine = ScopeCoroutine(newContext, uCont)
return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
}
// FAST PATH #2 – the new dispatcher is the same as the old one (something else changed)
// equals is used by design (see equals implementation is wrapper context like ExecutorCoroutineDispatcher)
if (newContext[ContinuationInterceptor] == oldContext[ContinuationInterceptor]) {
val coroutine = UndispatchedCoroutine(newContext, uCont)
// There are changes in the context, so this thread needs to be updated
withCoroutineContext(newContext, null) {
return@sc coroutine.startUndispatchedOrReturn(coroutine, block)
}
}
// SLOW PATH – use new dispatcher
val coroutine = DispatchedCoroutine(newContext, uCont)
coroutine.initParentJob()
block.startCoroutineCancellable(coroutine, coroutine)
coroutine.getResult()
}
}

4.4 coroutineScope{}

如3.2所示,coroutineScope{}会创建一个CoroutineScope{},并执行{}内新建的协程,不会阻塞当前线程。

4.5 runBlocking{}

启动一个新的协程并阻塞调用它的线程,会把挂起函数的结果作为协程的结果返回,常用来main方法和测试(所以Android开发中不常用)。

4.6 produce<> { }

启动一个新的协程,并生产一系列数据到channel中,这个协程返回ReceiveChannel,其他协程可从ReceiveChannel中接受数据。

五、CoroutineContext

5.1 定义

CoroutineContext是一系列元素的集合,主要的元素是代表协程的Job,此外还有协程的dispatcher等(Job、Dispatchers与CoroutineName都实现了Element接口)。 CoroutineScope封装了CoroutineContext:

public interface CoroutineScope {
public val coroutineContext: CoroutineContext
}

5.2 继承性

当通过CoroutineScope.launch启动一个新的协程时,新的CoroutineScope继承了外面的CoroutineContext,并且新的协程Job成为了父协程的子Job,这样当父Job取消时会递归取消子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

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。

image

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
46)]

最后

针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。

[外链图片转存中…(img-KzYWEgDr-1712652338846)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-3ufKyEon-1712652338847)]

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐