结尾
- 腾讯T4级别Android架构技术脑图;查漏补缺,体系化深入学习提升
- 一线互联网Android面试题含详解(初级到高级专题)
这些题目是今年群友去腾讯、百度、小米、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整理了答案,熟悉这些知识点会大大增加通过前两轮技术面试的几率
有Android开发3-5年基础,希望突破瓶颈,成为架构师的小伙伴,可以关注我
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
协程是一个并发方案。也是一种思想。
传统意义上的协程是单线程的,面对io密集型任务他的内存消耗更少,进而效率高。但是面对计算密集型的任务不如多线程并行运算效率高。
不同的语言对于协程都有不同的实现,甚至同一种语言对于不同平台的操作系统都有对应的实现。
我们kotlin语言的协程是 coroutines for jvm的实现方式。底层原理也是利用java 线程。
基础知识
生态架构
相关依赖库
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"
}
为什么一些人总觉得协程晦涩难懂?
1.网络上没有详细的关于协程的概念定义,每种语言、每个系统对其实现都不一样。可谓是众说纷纭,什么内核态用户态巴拉巴拉,很容易给我们带偏
2.kotlin的各种语法糖对我们造成的干扰。如:
- 高阶函数
- 源码实现类找不到
所以扎实的kotlin语法基本功是学习协程的前提。
实在看不懂得地方就反编译为java,以java最终翻译为准。
协程是什么?有什么用?
kotlin中的协程干的事就是把异步回调代码拍扁了,捋直了,让异步回调代码同步化。除此之外,没有任何特别之处。
创建一个协程,就是编译器背后偷偷生成一系列代码,比如说状态机。
通过挂起和恢复让状态机状态流转实现把层层嵌套的回调代码变成像同步代码那样直观、简洁。
它不是什么线程框架,也不是什么高深的内核态,用户态。它其实对于咱们安卓来说,就是一个关于回调函数的语法糖。。。
本文将会围绕挂起与恢复彻底剖析协程的实现原理
Kotlin函数基础知识复习
再Kotlin中函数是一等公民,有自己的类型
函数类型
fun foo(){}
//类型为 () -> Unit
fun foo(p: Int){}
//类型为 (Int) -> String
class Foo{
fun bar(p0: String,p1: Long):Any{}
}
//那么 bar 的类型为:Foo.(String,Long) -> Any
//Foo就是bar的 receiver。也可以写成 (Foo,String,Long) ->Any
函数引用
fun foo(){}
//引用是 ::foo
fun foo(p0: Int): String
//引用也是 ::foo
咋都一样?没办法,就这样规定的。使用的时候 只能靠编译器推断
val f: () -> Unit = ::foo //编译器会推断出是fun foo(){}
val g: (Int) -> String = ::foo //推断为fun foo(p0: Int): String
带Receiver的写法
class Foo{
fun bar(p0: String,p1: Long):Any{}
}
val h: (Foo,String,Long) -> Any = Foo:bar
绑定receiver的函数引用:
val foo: Foo = Foo()
val m: (String,Long) -> Any = foo:bar
额外知识点
val x: (Foo,String,Long) -> Any = Foo:bar
val y: Function3<Foo,String,Long,Any> = x
Foo.(String,Long) -> Any = (Foo,String,Long) ->Any = Function3<Foo,String,Long,Any>
函数作为参数传递
fun yy(p: (Foo,String,Long)->Any){
p(Foo(),"Hello",3L)//直接p()就能调用
//p.invoke(Foo(),"Hello",3L) 也可以用invoke形式
}
Lambda
就是匿名函数,它跟普通函数比是没有名字的,听起来好像是废话
//普通函数
fun func(){
println("hello");
}
//去掉函数名 func,就成了匿名函数
fun(){
println("hello");
}
//可以赋值给一个变量
val func = fun(){
println("hello");
}
//匿名函数的类型
val func :()->Unit = fun(){
println("hello");
}
//Lambda表达式
val func={
print("Hello");
}
//Lambda类型
val func :()->String = {
print("Hello");
"Hello" //如果是Lambda中,最后一行被当作返回值,能省掉return。普通函数则不行
}
//带参数Lambda
val f1: (Int)->Unit = {p:Int ->
print(p);
}
//可进一步简化为
val f1 = {p:Int ->
print(p);
}
//当只有一个参数的时候,还可以写成
val f1: (Int)->Unit = {
print(it);
}
关于函数的个人经验总结
函数跟匿名函数看起来没啥区别,但是反编译为java后还是能看出点差异
如果只是用普通的函数,那么他跟普通java 函数没啥区别。
比如 fun a()
就是对应java方法public void a(){}
但是如果通过函数引用(:: a)来用这个函数,那么他并不是直接调用fun a()
而是重新生成一个Function0
挂起函数
suspend 修饰。
挂起函数中能调用任何函数。
非挂起函数只能调用非挂起函数。
换句话说,suspend函数只能在suspend函数中调用。
简单的挂起函数展示:
//com.example.studycoroutine.chapter.CoroutineRun.kt
suspend fun suspendFun(): Int {
return 1;
}
挂起函数特殊在哪?
public static final Object suspendFun(Continuation completion) {
return Boxing.boxInt(1);
}
这下理解suspend为啥只能在suspend里面调用了吧?
想要让道貌岸然的suspend函数干活必须要先满足它!!!就是给它里面塞入一颗球。
然后他想调用其他的suspend函数,只需将球继续塞到其它的suspend方法里面。
普通函数里没这玩意啊,所以压根没法调用suspend函数。。。
读到这里,想必各位会有一些疑问:
- question1.这不是鸡生蛋生鸡的问题么?第一颗球是哪来的?
- question2.为啥编译后返回值也变了?
- question3.suspendFun 如果在协程体内被调用,那么他的球(completion)是谁?
标准库给我们提供的最原始工具
public fun <T> (suspend () -> T).startCoroutine(completion: Continuation<T>) {
createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}
public fun <T> (suspend () -> T).createCoroutine(completion: Continuation<T>): Continuation<Unit> =
SafeContinuation(createCoroutineUnintercepted(completion).intercepted(), COROUTINE_SUSPENDED)
以一个最简单的方式启动一个协程。
Demo-K1
fun main() {
val b = suspend {
val a = hello2()
a
}
b.createCoroutine(MyCompletionContinuation()).resume(Unit)
}
suspend fun hello2() = suspendCoroutine<Int> {
thread{
Thread.sleep(1000)
it.resume(10086)
}
}
class MyContinuation() : Continuation<Int> {
override val context: CoroutineContext = CoroutineName("Co-01")
override fun resumeWith(result: Result<Int>) {
log("MyContinuation resumeWith 结果 = ${result.getOrNull()}")
}
}
两个创建协程函数区别
startCoroutine 没有返回值 ,而createCoroutine返回一个Continuation,不难看出是SafeContinuation
好像看起来主要的区别就是startCoroutine直接调用resume(Unit),所以不用包装成SafeContinuation,而createCoroutine则返回一个SafeContinuation,因为不知道将会在何时何处调用resume,必须保证resume只调用一次,所以包装为safeContinuation
SafeContinuationd的作用是为了确保只有发生异步调用时才挂起
分析createCoroutineUnintercepted
//kotlin.coroutines.intrinsics.CoroutinesIntrinsicsH.kt
@SinceKotlin("1.3")
public expect fun <T> (suspend () -> T).createCoroutineUnintercepted(completion: Continuation<T>): Continuation<Unit>
先说结论
其实可以简单的理解为kotlin层面的原语,就是返回一个协程体。
开始分析
引用代码Demo-K1首先b 是一个匿名函数,他肯定要被编译为一个FunctionX,同时它还被suspend修饰 所以它肯定跟普通匿名函数编译后不一样。
编译后的源码为
public static final void main() {
Function1 var0 = (Function1)(new Function1((Continuation)null) {
int label;
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
Object var10000;
switch(this.label) {
case 0:
ResultKt.throwOnFailure($result);
this.label = 1;
var10000 = TestSampleKt.hello2(this);
if (var10000 == var3) {
return var3;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
int a = ((Number)var10000).intValue();
return Boxing.boxInt(a);
}
@NotNull
public final Continuation create(@NotNull Continuation completion) {
Intrinsics.checkParameterIsNotNull(completion, "completion");
Function1 var2 = new <anonymous constructor>(completion);
return var2;
}
public final Object invoke(Object var1) {
return((<undefinedtype>)this.create((Continuation)var1)).invokeSuspend(Unit.INSTANCE);
}
});
boolean var1 = false;
Continuation var7 = ContinuationKt.createCoroutine(var0, (Continuation)(newMyContinuation()));
Unit var8 = Unit.INSTANCE;
boolean var2 = false;
Companion var3 = Result.Companion;
boolean var5 = false;
Object var6 = Result.constructor-impl(var8);
var7.resumeWith(var6);
}
我们可以看到先是 Function1 var0 = new Function1
创建了一个对象,此时跟协程没关系,这步只是编译器层面的匿名函数语法优化
如果直接
fun main() {
suspend {
val a = hello2()
a
}.createCoroutine(MyContinuation()).resume(Unit)
}
也是一样会创建Function1 var0 = new Function1
解答question1
尾声
在我的博客上很多朋友都在给我留言,需要一些系统的面试高频题目。之前说过我的复习范围无非是个人技术博客还有整理的笔记,考虑到笔记是手写版不利于保存,所以打算重新整理并放到网上,时间原因这里先列出面试问题,题解详见:
展示学习笔记
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
ion1
尾声
在我的博客上很多朋友都在给我留言,需要一些系统的面试高频题目。之前说过我的复习范围无非是个人技术博客还有整理的笔记,考虑到笔记是手写版不利于保存,所以打算重新整理并放到网上,时间原因这里先列出面试问题,题解详见:
[外链图片转存中…(img-In7mxJL0-1715452630112)]
展示学习笔记
[外链图片转存中…(img-cy9yRWAj-1715452630112)]
[外链图片转存中…(img-xv8GviJP-1715452630113)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!