2024年Android最全史上最详Android版kotlin协程入门进阶实战(四),面试阿里巴巴国际站运营会问什么问题

题外话

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多程序员朋友无法获得正确的资料得到学习提升,故此将并将重要的Android进阶资料包括自定义view、性能优化、MVC与MVP与MVVM三大框架的区别、NDK技术、阿里面试题精编汇总、常见源码分析等学习资料。

【Android思维脑图(技能树)】

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

希望我能够用我的力量帮助更多迷茫、困惑的朋友们,帮助大家在IT道路上学习和发展~

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

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

this,

SupervisorJob() + Dispatchers.Main.immediate

)

if (mInternalScopeRef.compareAndSet(null, newScope)) {

newScope.register()

return newScope

}

}

}

我们可以看到lifecycleScope采用的和MainScope一样的创建CoroutineScope,同时它又通过结合lifecycle来实现当lifecycle状态处于DESTROYED状态的时候自动关闭所有的协程。

public abstract class LifecycleCoroutineScope internal constructor() : CoroutineScope {

internal abstract val lifecycle: Lifecycle

public fun launchWhenCreated(block: suspend CoroutineScope.() -> Unit): Job = launch {

lifecycle.whenCreated(block)

}

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

lifecycle.whenStarted(block)

}

public fun launchWhenResumed(block: suspend CoroutineScope.() -> Unit): Job = launch {

lifecycle.whenResumed(block)

}

}

internal class LifecycleCoroutineScopeImpl(

override val lifecycle: Lifecycle,

override val coroutineContext: CoroutineContext

) : LifecycleCoroutineScope(), LifecycleEventObserver {

init {

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()

}

}

}

同时我们也可以通过launchWhenCreatedlaunchWhenStartedlaunchWhenResumed来启动协程,等到lifecycle处于对应状态时自动触发此处创建的协程。

比如我们可以这么操作:

class MainTestActivity : AppCompatActivity() {

init {

lifecycleScope.launchWhenResumed {

Log.d(“init”, “在类初始化位置启动协程”)

}

}

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

}

}

D/onResume: onResume

D/init: 在类初始化位置启动协程

按照我们正常情况加载顺序,是不是应该init先执行输出?然而在实际情况中它是在等待Activity进入onResume状态以后才执行接着看launchWhenResumed中调用的whenResumed实现。

public suspend fun Lifecycle.whenResumed(block: suspend CoroutineScope.() -> T): T {

return whenStateAtLeast(Lifecycle.State.RESUMED, block)

}

public suspend fun Lifecycle.whenStateAtLeast(

minState: Lifecycle.State,

block: suspend CoroutineScope.() -> T

): 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()

}

}

@MainThread

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) {

handleDestroy(parentJob)

} else if (source.lifecycle.currentState < minState) {

dispatchQueue.pause()

} else {

dispatchQueue.resume()

}

}

init {

if (lifecycle.currentState == Lifecycle.State.DESTROYED) {

handleDestroy(parentJob)

} else {

lifecycle.addObserver(observer)

}

}

private inline fun handleDestroy(parentJob: Job) {

parentJob.cancel()

finish()

}

@MainThread

fun finish() {

lifecycle.removeObserver(observer)

dispatchQueue.finish()

}

}

我们可以看到,实际上是调用了whenStateAtLeast,同时使用了withContext进行了一个同步操作。然后在LifecycleController中通过添加LifecycleObserver来监听状态,通过lifecycle当前状态来对比我们设定的触发状态,最终决定是否恢复执行。

现在我们对于Activity中的lifecycleScope的创建以及销毁流程有了一个大概的了解。同理Fragment中的lifecycleScope实现原理也是和Activity是一样的,这里我们就不再重复讲解。我们做个简单的实验:

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

lifecycleScope.launch {

delay(2000)

Toast.makeText(this@MainActivity,“haha”,Toast.LENGTH_SHORT).show()

}

}

}

这个时候是不是比之前的使用方式简单多了,我们既不用关心创建过程,也不用关心销毁的过程。

这个时候我们就需要提到CoroutineExceptionHandler协程异常处理。通过之前的章节我们知道,启动一个协程以后,如果未在协程上下文中添加CoroutineExceptionHandler情况下,一旦产生了未捕获的异常,那么我们的程序将会崩溃退出。

class MainActivity : AppCompatActivity() {

val exceptionHandler = CoroutineExceptionHandler { coroutineContext, throwable ->

Log.d(“exceptionHandler”, “${coroutineContext[CoroutineName]} $throwable”)

}

fun load() {

lifecycleScope.launch(exceptionHandler) {

//省略…

}

lifecycleScope.launch(exceptionHandler) {

//省略…

}

lifecycleScope.launch(exceptionHandler) {

//省略…

}

}

}

当出现这种情况的时候,像笔者这种有严重偷懒情结的人就开始抓狂了。为什么要写这么多遍 lifecycleScope.launch,同时每一次启动都要手动添加CoroutineExceptionHandler。难道就不能再简便一点吗?

image.png

当然可以,首先我们自定义一个异常处理,,我们在实现上只做一个简单的异常日志输出:

/**

  • @param errCode 错误码

  • @param errMsg 简要错误信息

  • @param report 是否需要上报

*/

class GlobalCoroutineExceptionHandler(private val errCode: Int, private val errMsg: String = “”, private val report: Boolean = false) : CoroutineExceptionHandler {

override val key: CoroutineContext.Key<*>

get() = CoroutineExceptionHandler

override fun handleException(context: CoroutineContext, exception: Throwable) {

val msg = exception.stackTraceToString()

Log.e(“ e r r C o d e " , " G l o b a l C o r o u t i n e E x c e p t i o n H a n d l e r : errCode","GlobalCoroutineExceptionHandler: errCode","GlobalCoroutineExceptionHandler:{msg}”)

}

}

然后我们在通过kotlin的扩展函数来简化我们的使用,去掉重复写lifecycleScope.launchexceptionHandler的过程,我们就定义三个常用方法。

inline fun AppCompatActivity.requestMain(

errCode: Int = -1, errMsg: String = “”, report: Boolean = false,

crossinline block: suspend CoroutineScope.() -> Unit) {

lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {

block.invoke(this)

}

}

inline fun AppCompatActivity.requestIO(

errCode: Int = -1, errMsg: String = “”, report: Boolean = false,

crossinline block: suspend CoroutineScope.() -> Unit): Job {

return lifecycleScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {

block.invoke(this)

}

}

inline fun AppCompatActivity.delayMain(

errCode: Int = -1, errMsg: String = “”, report: Boolean = false,

delayTime: Long, crossinline block: suspend CoroutineScope.() -> Unit) {

lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {

withContext(Dispatchers.IO) {

delay(delayTime)

}

block.invoke(this)

}

}

这个时候我们就可以愉快的在Activity中使用了

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

requestMain {

delay(2000)

Toast.makeText(this@MainActivity,“haha”,Toast.LENGTH_SHORT).show()

}

requestIO {

loadNetData()

}

delayMain(100){

Toast.makeText(this@MainActivity,“haha”,Toast.LENGTH_SHORT).show()

}

}

private suspend fun loadNetData(){

//网络加载

}

}

同样的我们再扩展一套基于Fragment的方法

inline fun Fragment.requestMain(

errCode: Int = -1, errMsg: String = “”, report: Boolean = false,

crossinline block: suspend CoroutineScope.() -> Unit) {

lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {

block.invoke(this)

}

}

inline fun Fragment.requestIO(

errCode: Int = -1, errMsg: String = “”, report: Boolean = false,

crossinline block: suspend CoroutineScope.() -> Unit) {

lifecycleScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {

block.invoke(this)

}

}

inline fun Fragment.delayMain(

errCode: Int = -1, errMsg: String = “”, report: Boolean = false, delayTime: Long,

crossinline block: suspend CoroutineScope.() -> Unit) {

lifecycleScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {

withContext(Dispatchers.IO) {

delay(delayTime)

}

block.invoke(this)

}

}

然后也可以愉快的在Fragment中使用了

class HomeFragment:Fragment() {

init {

lifecycleScope.launchWhenCreated {

Toast.makeText(context,“Fragment创建了”, Toast.LENGTH_SHORT).show()

}

}

override fun onCreateView(

inflater: LayoutInflater,

container: ViewGroup?,

savedInstanceState: Bundle?

): View? {

return inflater.inflate(R.layout.fragment_main,container,false)

}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {

super.onViewCreated(view, savedInstanceState)

requestMain {

//…

}

requestIO {

//…

}

delayMain(100){

//…

}

}

}

这里需要提一下,可能有的人不太明白,为什么要把ActivityFragment都分开写,他们都是使用的lifecycleScope,我们直接通过lifecycleScope扩展就不可以了吗。假如我们这么扩展:

inline fun LifecycleCoroutineScope.requestMain(

errCode: Int = -1, errMsg: String = “”, report: Boolean = false,

crossinline block: suspend CoroutineScope.() -> Unit) {

launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {

block.invoke(this)

}

}

复制代码

我们以Dailog为例,来启动一个协程:

val dialog = Dialog(this)

dialog.show()

(dialog.context as LifecycleOwner).lifecycleScope.requestMain {

withContext(Dispatchers.IO){

//网络加载

}

// 刷新UI

}

dialog.cancel()

那么可能会出现一个什么问题?是的,内存泄露的问题以及错误的引用问题。虽然我的dialog被销毁了,但是我们lifecycleScope并不处于DESTROYED状态,所以我们的协程依然会执行,这个时候我们就会出现内存泄露和崩溃问题。

通过上面的学习,我们已经基本掌握了协程在ActivityFragment中的使用方式。接下来我们讲解在Viewmodel中使用协程。

ViewModel中使用协程


如果我们想和在ActivityFragment中一样的简便、快速的在ViewModel使用协程。那么我们就需要集成下面这个官方的ViewModel扩展库。

implementation “androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1”

ActivityFragment不同的是,在ViewModel我们使用的不是lifecycleScope,而是使用viewModelScope,使用viewModelScope,使用viewModelScope。重要的事情说三遍。

这里一定要注意噢,之前就有好几个人问我为什么在viewmodel里面用不了协程,我开始纳闷半天咋就用不了呢。最后一问结果是_在ViewModel使用lifecycleScope_,这样做是不对滴

public 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相比较lifecycleScope实现会稍微简单一点。都是使用的SupervisorJob() + Dispatchers.Main上下文,同时最终的取消操作也类似lifecycleScope,只不过viewModelScope取消是在ViewModel的销毁的时候取消。

final void clear() {

mCleared = true;

if (mBagOfTags != null) {

synchronized (mBagOfTags) {

for (Object value : mBagOfTags.values()) {

closeWithRuntimeException(value);

}

}

}

onCleared();

}

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) {

closeWithRuntimeException(result);

}

return result;

}

private static void closeWithRuntimeException(Object obj) {

if (obj instanceof Closeable) {

try {

((Closeable) obj).close();

} catch (IOException e) {

throw new RuntimeException(e);

}

}

}

同样的通过上面的总结,我们也为ViewModel扩展一套常用的方法

inline fun ViewModel.requestMain(

errCode: Int = -1, errMsg: String = “”, report: Boolean = false,

crossinline block: suspend CoroutineScope.() -> Unit) {

viewModelScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {

block.invoke(this)

}

}

inline fun ViewModel.requestIO(

errCode: Int = -1, errMsg: String = “”, report: Boolean = false,

crossinline block: suspend CoroutineScope.() -> Unit) {

viewModelScope.launch(Dispatchers.IO + GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {

block.invoke(this)

}

}

inline fun ViewModel.delayMain(

errCode: Int = -1, errMsg: String = “”, report: Boolean = false, delayTime: Long,

crossinline block: suspend CoroutineScope.() -> Unit) {

viewModelScope.launch(GlobalCoroutineExceptionHandler(errCode, errMsg, report)) {

withContext(Dispatchers.IO) {

delay(delayTime)

}

block.invoke(this)

}

}

然后我们就可以愉快的在ViewModel进行使用协程了。

class MainViewModel:ViewModel() {

init {

requestMain {

Log.d(“MainViewModel”, “主线程中启动协程”)

}

requestIO {

Log.d(“MainViewModel”, “IO线程中启动协程进行网络加载”)

}

delayMain(100){

Log.d(“MainViewModel”, “主线程中启动协程并延时一定时间”)

}

}

}

好了,常规使用协程的方式我都已经学会。但是我们在一些环境下如法使用使用lifecycleScopeviewModelScope的时候我们又该怎么办。比如:在ServiceDialogPopWindow以及一些其他的环境中又该如何使用。

其他环境下使用协程


在这些环境中我们可以采用通用的方式进行处理,其实还是根据协程作用域的差异分为两类:

  • 协同作用域:这一类我们就模仿MainScope自定义一个CoroutineScope

  • 主从(监督)作用域:这一类我们直接使用MainScope,然后在此基础上做一些扩展即可。

如果对这两个概念还不理解的,麻烦移步到第二章节里面仔细阅读一遍,这里就不再解释。

我们接下来模仿MainScope创建一个CoroutineScope,它是在主线程下执行,并且它的Job不是SupervisorJob

@Suppress(“FunctionName”)

public fun NormalScope(): CoroutineScope = CoroutineScope(Dispatchers.Main)

总结

算法知识点繁多,企业考察的题目千变万化,面对越来越近的“金九银十”,我给大家准备好了一套比较完善的学习方法,希望能帮助大家在有限的时间里尽可能系统快速的恶补算法,通过高效的学习来提高大家面试中算法模块的通过率。

这一套学习资料既有文字档也有视频,里面不仅仅有关键知识点的整理,还有案例的算法相关部分的讲解,可以帮助大家更好更全面的进行学习,二者搭配起来学习效果会更好。

部分资料展示:




有了这套学习资料,坚持刷题一周,你就会发现自己的算法知识体系有明显的完善,离大厂Offer的距离更加近。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

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

  • 主从(监督)作用域:这一类我们直接使用MainScope,然后在此基础上做一些扩展即可。

如果对这两个概念还不理解的,麻烦移步到第二章节里面仔细阅读一遍,这里就不再解释。

我们接下来模仿MainScope创建一个CoroutineScope,它是在主线程下执行,并且它的Job不是SupervisorJob

@Suppress(“FunctionName”)

public fun NormalScope(): CoroutineScope = CoroutineScope(Dispatchers.Main)

总结

算法知识点繁多,企业考察的题目千变万化,面对越来越近的“金九银十”,我给大家准备好了一套比较完善的学习方法,希望能帮助大家在有限的时间里尽可能系统快速的恶补算法,通过高效的学习来提高大家面试中算法模块的通过率。

这一套学习资料既有文字档也有视频,里面不仅仅有关键知识点的整理,还有案例的算法相关部分的讲解,可以帮助大家更好更全面的进行学习,二者搭配起来学习效果会更好。

部分资料展示:

[外链图片转存中…(img-koGeM6Vv-1714885169898)]
[外链图片转存中…(img-3Lyb7EAY-1714885169899)]
[外链图片转存中…(img-ihtuF1Tw-1714885169899)]
[外链图片转存中…(img-mlZqZvd0-1714885169900)]

有了这套学习资料,坚持刷题一周,你就会发现自己的算法知识体系有明显的完善,离大厂Offer的距离更加近。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

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

  • 23
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值