背景
在深度使用 Flutter 开发过程中,我们遇到了业务代码耦合严重,代码可维护性糟糕,如入泥泞。我们需要一个统一的应用框架来摆脱当下的开发困境,而这也是 Flutter 领域空缺的一块处女地。
Fish Redux 是为解决上面问题上层应用框架,它是一个基于 Redux 数据管理的组装式 flutter 应用框架, 特别适用于构建中大型的复杂应用。
它的最大特点是配置式组装, 一方面将一个大的页面,对视图和数据层层拆解为互相独立的 Component|Adapter,上层负责组装,下层负责实现,另一方面将 Component|Adapter 拆分为 View,Reducer,Effect 等相互独立的上下文无关函数。所以它会非常干净,易编写、易维护、易协作。
Fish Redux 的灵感主要来自于 Redux、React、Elm、Dva 这样的优秀框架,而 Fish Redux 站在巨人的肩膀上,将集中,分治,复用,隔离做的更进一步。
1、MVVM架构模式概览
这是使用MVVM架构模式+Kotlin协程+JetPack(ViewModel+LiveData)+Retrofit的架构,实现WanAndroid登录接口的小DEMO,后续会慢慢完善WanAndroid客户端
1、ViewModel
为了从界面控制器Activity/Fragment逻辑中分离出视图View数据所有权,架构组件为界面控制器提供了 ViewModel 辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个 Activity 或 Fragment 实例使用。
2、LiveData
LiveData 是一种可观察的数据存储器类,具有生命周期感知能力,意指它遵循其他应用组件如 Activity、Fragment 或 Service 生命周期,可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。LiveData 对象通常存储在 ViewModel 对象中,并可通过 getter 方法进行访问。
3、Kotlin协程
协程依附在线程上,可以实现顺序编写异步代码,自动进行线程切换。并且ViewModelScope为应用中的每个 ViewModel 定义了 ViewModelScope。如果 ViewModel 已清除,则在此范围内启动的协程都会自动取消。
4、Retrofit
将服务接口中的网络请求函数声明为suspend挂起接口函数,以支持Kotlin线程,并将suspend函数结果作为 LiveData 对象传送。
2、ViewModel
//获取ViewModel
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)`
ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProvider 的 Lifecycle。ViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。
3、LiveData
//对User数据进行观察
viewModel.user.observe(this, Observer {
//展示登录结果
if (it.errorCode == 0) {
Toast.makeText(this, it.data?.nickname, Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, it.errorMsg, Toast.LENGTH_SHORT).show()
}
})
使用 LiveData 具有以下优势:确保界面符合数据状态
LiveData 遵循观察者模式。当生命周期状态发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。观察者可以在每次发生更改时更新界面,而不是在每次应用数据发生更改时更新界面。
不会发生内存泄漏
观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
不会因 Activity 停止而导致崩溃
如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。
不再需要手动处理生命周期
界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
数据始终保持最新状态
如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。例如,曾经在后台的 Activity 会在返回前台后立即接收最新的数据。
适当的配置更改
如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
共享资源
可以使用单一实例模式扩展 LiveData 对象以封装系统服务,以便在应用*享它们。
4、Kotlin协程
4.1、异步的本质
什么是异步?
异步就是同时进行一个以上彼此目的不同的任务。
但是对于有前后依赖关系的任务,异步该如何处理呢?
利用异步中的回调机制处理。
为什么需要异步回调机制?
因为不同的任务之间存在前后的依赖关系。
异步回调机制有什么缺点?
代码结构过分耦合,遇到多重函数回调的嵌套耦合,也就是回调地狱,代码会难以维护。
解决回调地狱的方案有什么?
链式调用结构。
常见方式就是使用RxJava,它是反应函数式编程在Java中的实现。
但是RxJava中流的创建、转化与消费都需要使用到各种类和丰富的操作符,加大了RxJava的学习成本。
减少在无封装情况下使用RxJava,因为你无法保证团队里面的每一个成员都能看懂它,并且在修改时都能做出正确选择。
在串行的执行中,虽然代码确实是顺序执行的,但其实是在不同的线程上顺序执行的。那为什么在串行的执行中代码执行顺序一致,却还要使用回调呢?
因为串行的执行中,执行是阻塞式的,主线程的阻塞会导致很严重的问题,所以所有的耗时操作不能在主线程中执行,所以就需要多线程并行来执行。
在并行的执行中,异步回调其实就是代码的多线程顺序执行。那能不能既按照顺序的方式编写代码,又可以让代码在不同的线程顺序执行,自动完成线程的切换工作呢?
那就是Kotlin协程。
Kotlin 的协程是一种无栈协程的实现,它的控制流转依靠对协程体本身编译生成的状态机的状态流转来实现,变量保存也是通过闭包语法来实现的。
结论:
异步回调就是代码的多线程顺序执行,而Kotlin协程可以实现顺序编写异步代码,自动进行线程切换。
那么协程自动进行线程切换的原理是什么?
Yield:让出CPU,放弃调度控制权,回到上一次Resume的地方
Resume:获取调度控制权,继续执行程序,到上一次Yield的地方
例子:
1. GlobalScope.launch发起了一个协程,并在IO线程上执行,
2\. 在协程里,去调用接口获取结果。
3. 拿到结果,使用withContext(Dispatchers.Main)切换到主线程并更新界面
4.2、协程的类型
是协程范围,指的是协程内的代码运行的时间周期范围,如果超出了指定的协程范围,协程会被取消执行。
GlobalScope
指的是与应用进程相同的协程范围,也就是在进程没有结束之前协程内的代码都可以运行。
JetPack中提供的生命周期感知型协程范围:
ViewModelScope,为应用中的每个 ViewModel 定义了 ViewModelScope。如果 ViewModel 已清除,则在此范围内启动的协程都会自动取消。
LifecycleScope,为每个 Lifecycle 对象定义了 LifecycleScope。在此范围内启动的协程会在 Lifecycle 被销毁时取消。
使用 LiveData 时,可能需要异步计算值。可以使用 liveData 构建器函数调用 suspend 函数,并将结果作为 LiveData 对象传送。
相关链接:https://developer.android.google.cn/topic/libraries/architecture/coroutines
4.3、协程的启动
launch方法:
/**
* 重要知识:ViewModel+协程
*/
fun ViewModel.launch(
block: suspend CoroutineScope.() -> Unit,
onError: (e: Throwable) -> Unit = {},
onComplete: () -> Unit = {}
) {
viewModelScope.launch(CoroutineExceptionHandler { _, e -> onError(e) }) {
try {
block.invoke(this)
} finally {
onComplete()
}
}
}
源码:
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.3.1、launch方法解释
context
协程上下文,可以指定协程运行的线程。默认与指定的CoroutineScope中的coroutineContext保持一致,比如GlobalScope默认运行在一个后台工作线程内。也可以通过显示指定参数来更改协程运行的线程,Dispatchers提供了几个值可以指定:Dispatchers.Default、Dispatchers.Main、Dispatchers.IO、Dispatchers.Unconfined。
start
协程的启动模式。默认的CoroutineStart.DEFAULT是指协程立即执行,除此之外还有CoroutineStart.LAZY、CoroutineStart.ATOMIC、CoroutineStart.UNDISPATCHED。
block
协程主体。也就是要在协程内部运行的代码,可以通过lamda表达式的方式方便的编写协程内运行的代码。
CoroutineExceptionHandler
指定CoroutineExceptionHandler来处理协程内部的异常。
Job
返回值,对当前创建的协程的引用。可以通过Job的start、cancel、join等方法来控制协程的启动和取消。
4.4、suspend挂起函数
suspend关键字只起到了标志这个函数是一个耗时操作,必须放在协程中执行的作用,而withContext方法则进行了线程的切换工作。
协程中的代码自动地切换到其他线程之后又自动地切换回了主线程!顺序编写保证了逻辑上的直观性,协程的自动线程切换又保证了代码的非阻塞性。挂起函数必须在协程或者其他挂起函数中被调用,也就是挂起函数必须直接或者间接地在协程中执行。
那为什么协程中的代码没有在主线程中执行呢?而且执行完毕为什么还会自动地切回主线程呢?
协程的挂起可以理解为协程中的代码离开协程所在线程的过程,协程的恢复可以理解为协程中的代码重新进入协程所在线程的过程。协程就是通过的这个挂起恢复机制进行线程的切换。
4.5、async await方法
用async方法包裹了suspend方法来执行并发请求,并发结果都返回之后,切换到主线程,接着再用await方法来获取并发请求结果。
5、Retrofit
HTTP接口suspend挂起函数:
interface ApiService {
@FormUrlEncoded
@POST("user/login")
suspend fun loginForm(@Field("username") username: String,@Field("password") password: String): BaseResponse<User>
}
kotlin泛型:
data class BaseResponse<T>(
val errorCode: Int=0,
val errorMsg:String? = null,
var data: T? = null
)
这是使用MVVM架构模式+Kotlin协程+JetPack(ViewModel+LiveData)+Retrofit的架构,实现WanAndroid登录接口的小DEMO,后续会慢慢完善WanAndroid客户端
如何做好面试突击,规划学习方向?
面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。
学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。
同时我还搜集整理2020年字节跳动,以及腾讯,阿里,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 Xmind(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。
在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多。
点击:《Android架构视频+BAT面试专题PDF+学习笔记》即可免费获取~
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
架构视频+BAT面试专题PDF+学习笔记》**](https://github.com/a120464/Android-P7/blob/master/Android%E5%BC%80%E5%8F%91%E4%B8%8D%E4%BC%9A%E8%BF%99%E4%BA%9B%EF%BC%9F%E5%A6%82%E4%BD%95%E9%9D%A2%E8%AF%95%E6%8B%BF%E9%AB%98%E8%96%AA%EF%BC%81.md)即可免费获取~
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。