皮皮网
皮皮网

【120源码怎么算】【c语言sax源码】【秒赞源码安装】viewmodel源码详解

来源:如何看mybatis源码 发表时间:2024-12-28 14:15:35

1.听说这套框架可以搞定 Android MVI
2.WPF MVVM实例一
3.最全vue面试必问题(附题)
4.LiveData 面试题库、码详解答、码详源码分析
5.lifecycleScope 和viewModelScope
6.Jetpack学习之----ViewModel

viewmodel源码详解

听说这套框架可以搞定 Android MVI

       前言

       没有最完美的码详架构,只有最合适的码详架构。

       Android应用架构变迁:MVC、码详MVP、码详120源码怎么算MVVM、码详MVI。码详

       关于这四种架构的码详概念、逻辑、码详实现方式与优劣,码详技术社区内优质文章不胜枚举,码详此处不再赘述。码详

       今天重点介绍如何利用Airbnb开源框架Mavericks快速实践MVI架构。码详

       主要弄清楚下面几个问题:

       Mavericks(MvRx): Android on Autopilot

       Mavericks是码详Aribnb开源的一款功能强大且易于学习的AndroidMVI框架。Mavericks以AndroidJetpack和KotlinCoroutines为基础搭建上层逻辑,在技术先进性和可持续方面毋庸置疑。至于框架实用性,相信接受了Airbnb、Tonal等大型APP长时间检验的Mavericks,不会让开发者失望。

       核心概念

       一个简单的计数界面只需要下面几行代码,既清晰又简洁。

       实践

       需求:利用WanAndroidAPI[1]实现搜索热词的列表展示(支持下拉刷新)

       接口:/hotkey/json

       1. 依赖2. 初始化

       Application内onCreate()函数中执行初始化。

       3. MavericksState

       定义MainState并添加两个属性:

       4. MavericksViewModel

       定义MainViewModel管理MainState,实现获取搜索热词函数。

       5. MavericksView

       创建MainFragment并实现MavericksView接口用于展示搜索热词列表,用户可以下拉刷新请求新数据。

       6. 效果源码

       Talk is cheap,Show me the code。

       /onlyloveyd/AndroidSamples

       划重点

       1. Async异步处理密封类,有四个子类:Uninitialized、Loading、Success、Fail,分别代表异步处理的4种状态。

       2. onAsync异步属性状态变化监听

       3. retainValue加载过程中或者加载失败后显示的数据。

       示例中,c语言sax源码我们将getHotKeys()函数内的retainValue去掉,界面更新数据时会有明显的闪动。

       4. 监听模式:DeliveryMode

       5. 状态监听防止崩溃

       为了防止回调时界面已经被销毁而导致程序奔溃,采用launchWhenStarted防御策略。

       总结

       简单好用,是选轮子的基本标准。

       上手Mavericks后,感觉代码层次清晰,集成方便,简单易用,符合好轮子的标准。

       对于鄙人这种不太爱自己折腾框架的躺平者而言,简直福音。下一步准备把之前写的WanAndroidClient用Mavericks改写下。

WPF MVVM实例一

       1. 新建WPF应用程序 "WPFMVVMExample".

       2. Model实现

       创建 "StudentModel" 类于 "Model" 文件夹,实现 INotifyPropertyChanged 接口,以支持属性值更改的通知.

       3. ViewModel实现

       在 "ViewModel" 文件夹内新建 "StudentViewModel" 类,定义 DelegateCommand 类实现 ICommand 接口. DelegateCommand 可与 Button 的 Command 属性绑定,实现命令的执行与可用性指示.

       4. MainWindow.xaml实现

       设计 "MainWindow.xaml" 界面,包含 "显示" 按钮等元素,并用 xaml 代码描述界面布局.

       5. 运行程序

       执行程序,点击 "显示" 按钮,数据自动绑定至界面显示.

       6. 说明

       在 WPF 中,MVVM 设计模式降低 UI 与逻辑代码耦合,易于界面更新. 使用数据绑定,数据变化自动通知界面,无需直接操作界面元素.

       MVVM 结构将界面(View)、逻辑处理(ViewModel)与业务模型(Model)分离,View 通过 DataContext 绑定 ViewModel,ViewModel 通过 Model 获取数据和命令执行.

       项目源码下载链接:百度网盘 - pan.baidu.com/s/BIKyd...

       提取码:h1iw

       技术群加入:添加微信 "mm",备注 "加群",获取技术支持与交流.

最全vue面试必问题(附题)

       1. 请解释MVVM。

       MVVM是Model-View-ViewModel的缩写,它将MVC中的Controller演变为ViewModel。Model层代表数据模型,View代表UI组件,秒赞源码安装ViewModel是View和Model层的桥梁,数据会绑定到viewModel层并自动将数据渲染到页面中,视图变化的时候会通知viewModel层更新数据。

       2. 请说明Vue的生命周期。

       beforeCreate是new Vue()之后触发的第一个钩子,在当前阶段data、methods、computed以及watch上的数据和方法都不能被访问。created在实例创建完成后发生,当前阶段已经完成了数据观测,也就是可以使用数据,更改数据,在这里更改数据不会触发updated函数。可以做一些初始数据的获取,在当前阶段无法与Dom进行交互,如果非要想,可以通过vm.$nextTick来访问Dom。beforeMount发生在挂载之前,在这之前template模板已导入渲染函数编译。而当前阶段虚拟Dom已经创建完成,即将开始渲染。在此时也可以对数据进行更改,不会触发updated。mounted在挂载完成后发生,在当前阶段,真实的Dom挂载完毕,数据完成双向绑定,可以访问到Dom节点,使用$refs属性对Dom进行操作。beforeUpdate发生在更新之前,也就是响应式数据发生更新,虚拟dom重新渲染之前被触发,你可以在当前阶段进行更改数据,不会造成重渲染。updated发生在更新完成之后,当前阶段组件Dom已完成更新。手机支付平台源码要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新。beforeDestroy发生在实例销毁之前,在当前阶段实例完全可以被使用,我们可以在这时进行善后收尾工作,比如清除计时器。destroyed发生在实例销毁之后,这个时候只剩下了dom空壳。组件已被拆解,数据绑定被卸除,监听被移出,子实例也统统被销毁。

       3. 你的接口请求一般放在哪个生命周期中?

       接口请求一般放在mounted中,但需要注意的是服务端渲染时不支持mounted,需要放到created中。

       4. 再说一下Computed和Watch。

       Computed本质是一个具备缓存的watcher,依赖的属性发生变化就会更新视图。适用于计算比较消耗性能的计算场景。当表达式过于复杂时,在模板中放入过多逻辑会让模板难以维护,可以将复杂的逻辑放入计算属性中处理。Watch没有缓存性,更多的是观察的作用,可以监听某些数据执行回调。当我们需要深度监听对象中的属性时,可以打开deep:true选项,这样便会对对象中的每一项进行监听。这样会带来性能问题,优化的话可以使用字符串形式监听,如果没有写到组件中,不要忘记使用unWatch手动注销哦。

       5. 请说明v-if和v-show的区别。

       当条件不成立时,v-if不会渲染DOM元素,v-show操作的-0.1010补码和源码是样式(display),切换当前DOM的显示和隐藏。

       6. Vue模版编译原理知道吗,能简单说一下吗?

       简单说,Vue的编译过程就是将template转化为render函数的过程。会经历以下阶段:生成AST树、优化codegen。首先解析模版,生成AST语法树(一种用JavaScript对象的形式来描述整个模板)。使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理。Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的DOM也不会变化。那么优化过程就是深度遍历AST树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。编译的最后一步是将优化后的AST树转换为可执行的代码。

       7. Vue2.x和Vue3.x渲染器的diff算法分别说一下。

       同级比较,再比较子节点。先判断一方有子节点一方没有子节点的情况(如果新的children没有子节点,将旧的子节点移除)。比较都有子节点的情况(核心diff)。递归比较子节点。正常Diff两个树的时间复杂度是O(n^3),但实际情况下我们很少会进行跨层级的移动DOM,所以Vue将Diff进行了优化,从O(n^3) -> O(n),只有当新旧children都为多个子节点时才需要用核心的Diff算法进行同层级比较。Vue2的核心Diff算法采用了双端比较的算法,同时从新旧children的两端开始进行比较,借助key值找到可复用的节点,再进行相关操作。相比React的Diff算法,同样情况下可以减少移动节点次数,减少不必要的性能损耗,更加的优雅。Vue3.x借鉴了 ivi算法和 inferno算法 在创建VNode时就确定其类型,以及在 mount/patch 的过程中采用位运算来判断一个VNode的类型,在这个基础之上再配合核心的Diff算法,使得性能上较Vue2.x有了提升。(实际的实现可以结合Vue3.x源码看。) 该算法中还运用了动态规划的思想求解最长递归子序列。

       8. 再说一下虚拟Dom以及key属性的作用。

       由于在浏览器中操作DOM是很昂贵的。频繁的操作DOM,会产生一定的性能问题。这就是虚拟Dom的产生原因。Vue2的Virtual DOM借鉴了开源库snabbdom的实现。Virtual DOM本质就是用一个原生的JS对象去描述一个DOM节点。是对真实DOM的一层抽象。(也就是源码中的VNode类,它定义在src/core/vdom/vnode.js中。) VirtualDOM映射到真实DOM要经历VNode的create、diff、patch等阶段。key的作用是尽可能的复用 DOM 元素。新旧 children 中的节点只有顺序是不同的时候,最佳的操作应该是通过移动元素的位置来达到更新的目的。需要在新旧 children 的节点中保存映射关系,以便能够在旧 children 的节点中找到可复用的节点。key也就是children中节点的唯一标识。

       9. Vue2.x组件通信有哪些方式?

       父子组件通信:父->子props,子->父 $on、$emit 获取父子组件实例 $parent、$children Ref 获取实例的方式调用组件的属性或者方法 Provide、inject 官方不推荐使用,但是写组件库时很常用 兄弟组件通信 Event Bus 实现跨组件通信 Vue.prototype.$bus = new Vue Vuex 跨级组件通信 Vuex $attrs、$listeners Provide、inject

       . v-model是如何实现双向绑定的?

       v-model是用来在表单控件或者组件上创建双向绑定的。

       他的本质是v-bind和v-on的语法糖。

       在一个组件上使用v-model,默认会为组件绑定名为value的prop和名为input的事件。

       . 怎样理解Vue的单向数据流?所有的prop都使得其父子prop之间形成了一个单向下行绑定:父级prop的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

       额外的,每次父级组件发生更新时,子组件中所有的prop都将刷新为最新的值。这意味着你不应该在一个子组件内部改变prop。如果你这样做了,Vue会在浏览器的控制台中发出警告。子组件想修改时,只能通过$emit派发一个自定义事件,父组件接收到后,由父组件修改。

       有两种常见的试图改变一个prop的情形:

       这个prop用来传递一个初始值;这个子组件接下来希望将其作为一个本地的prop数据来使用。在这种情况下,最好定义一个本地的data属性并将这个prop用作其初始值:

       这个prop以一种原始的值传入且需要进行转换。在这种情况下,最好使用这个prop的值来定义一个计算属性。

       . hash路由和history路由实现原理说一下。

       location.hash的值实际就是URL中#后面的东西。history实际采用了HTML5中提供的API来实现,主要有history.pushState()和history.replaceState()。

LiveData 面试题库、解答、源码分析

       LivaData 的面试题库与解答、源码分析

        作者:唐子玄

       1. LiveData 如何感知生命周期的变化?

       LiveData 在常规的观察者模式上附加了条件,若生命周期未达标,即使数据发生变化也不通知观察者。这通过 Lifecycle 实现,Lifecycle 是生命周期对应的类,提供了添加/移除生命周期观察者的方法,并定义了全部生命周期的状态及对应事件。要观察生命周期,需要实现 LifecycleEventObserver 接口,并注册给 Lifecycle。除了生命周期观察者外,还有数据观察者,数据观察者会与 LifecycleOwner 进行绑定。

       2. LiveData 是如何避免内存泄漏的?

       内存泄漏是因为长生命周期的对象持有了短生命周期对象。在观察 LiveData 数据的代码中,Observer 作为界面的匿名内部类,它会持有界面的引用,同时 Observer 被 LiveData 持有,LivData 被 ViewModel 持有,而 ViewModel 的生命周期比 Activity 长。最终的持有链导致内存泄漏。LiveData 帮助避免内存泄漏,在内部 Observer 会被包装成 LifecycleBoundObserver,这实现了生命周期感知能力,同时它还持有了数据观察者,具备了数据观察能力。

       3. LiveData 是粘性的吗?若是,它是怎么做到的?

       是的,LiveData 是粘性的。数据是持久的,意味着它不会因被消费而消失。当 LiveData 值更新时,会通知所有观察者。这一过程通过一个 Map 结构保存了所有观察者,并通过遍历 Map 并逐个调用 considerNotify() 方法实现。观察者会被包装在 LifecycleBoundObserver 中,它具备了生命周期感知能力,同时持有了数据观察者。当组件生命周期发生变化时,会尝试将最新值分发给该数据观察者。

       4. 粘性的 LiveData 会造成什么问题?怎么解决?

       粘性的 LiveData 可能导致数据重复消费或消费逻辑混乱。解决方案包括使用带消费记录的值、带有最新版本号的观察者、SingleLiveEvent 等。其中,使用 SingleLiveEvent 可以根据数据的分类(暂态数据或非暂态数据)来选择性地利用或避免粘性。

       5. 什么情况下 LiveData 会丢失数据?

       在高频数据更新的场景下使用 LiveData.postValue() 时,如果在这次调用和下次调用之间再次调用 postValue(),则会导致数据丢失,因为值先被缓存,再向主线程抛出分发值的任务。这与 LiveData 的设计和更新机制有关。

       6. 在 Fragment 中使用 LiveData 需注意些什么?

       在 Fragment 中使用 LiveData 时,应当使用 viewLifecycleOwner 而非 this。避免因生命周期不一致导致的额外订阅者问题。使用 SingleLiveEvent 可以解决数据重复消费问题。

       7. 如何变换 LiveData 数据及注意事项?

       androidx.lifecycle.Transformations 提供了变换 LiveData 数据的方法,如 map()。需要注意数据变换操作应避免阻塞主线程,可使用 CoroutineLiveData 来异步化数据变换。

lifecycleScope 和viewModelScope

       前序:

       通过《ViewModel中的简易协程:viewModelScope》的文章,联想到了lifecycleScope的使用。

       LifecycleScope,即具有生命周期的协程,是LifecycleOwner的扩展属性,与生命周期绑定,并在LifecycleOwner销毁时自动取消。

       引入使用:LifecycleScope作为Lifecycle的扩展属性,与LifecycleOwner绑定。在示例中,lifecycleScope默认主线程,可通过withContext指定线程。

       whenResumed与launchWhenResumed在执行时机上相似,关键区别在于它们在生命周期不同状态下的行为。

       lifecycleScope的源码分析揭示了它如何避免内存泄漏。lifecycleScope继承自LifecycleCoroutineScope,后者的register方法添加了LifecycleEventObserver监听,当生命周期状态变为destroyed时,监听被移除,协程取消。

       源码中的小技巧指出,当继承对象与返回对象不一致时,返回对象通常是继承对象的子类。这解释了lifecycleScope的生命周期管理。

       在其他开发场景中,可以借鉴源码中的监听机制来实现资源回收,避免内存泄漏。

       关于如何在特定生命周期执行协程,以lifecycleScope.launchWhenResumed为例,涉及LifecycleController和LifecycleEventObserver的使用。

       当调用whenResumed并传入具体生命周期状态时,创建LifecycleController并初始化监听。在回调中,当生命周期状态大于传入状态时,执行调度队列,开始协程执行。

       关于获取当前生命周期状态,涉及到Lifecycle相关知识。在不同组件(如Activity或Fragment)中,通过ComponentActivity的实现来派发生命周期状态。

       验证分析通过代码测试和源码调试,证实了以上流程的正确性。

       总结:lifecycleScope的使用及执行流程分析,揭示了其如何与生命周期绑定,避免内存泄漏,并在特定生命周期执行协程。

Jetpack学习之----ViewModel

        官方学习文档

        ViewModel就是存储页面相关的数据,并将这些数据和Activity、Fragment等有生命周期相关的组件相关联,赋予数据生命周期。

        特点:

        ViewModel的生命周期

        在viewModel对象创建时开始,一直到他所关联的界面控制器销毁时才销毁,这就说明了即使发生了横竖屏切换,界面相关的数据也是一直存在并且不受横竖屏切换的影响。

        通常我们是在Actvity的onCreate()方法中来创建ViewModel对象,该ViewModel对象会一直在内存中,直到这个Activity销毁时才释放资源。

        从上面ViewModel的工作原理可以得知:

        1、ViewModel 一旦创建好了,就会一直保存到当前界面控制器(Activity 、Fragment等)销毁时才会释放资源;

        2、不同的界面控制器,ViewModel 的对象时存在不同的Hashmap中的,他们也是不同的对象;局部单例;

        3、要做到全局单例ViewModel对象,可以将ViewModel放到Application中去;

        接下来从源码角度来分析一下原理:

        在构建Activity的对象时,在其父类ComponentActivity.java中实现了接口ViewModelStoreOwner,在其实现方法中生成ViewModelStore对象

        在界面控制器的构造函数中,就添加了对生命周期的观察者,而当观察者收到当前的界面控制器的生命周期是Lifecycle.Event.ON_DESTROY时,就会将mViewModelStore对象map中所有保存的viewModel清理掉,这样来达到释放资源。

        这里只处理了ON_DESTROY的生命周期状态,那么也就说明了在ViewModel对象实例创建成功后,不管界面控制器(如Activity)的生命周期(除ON_DESTROY外)如何发生变化,ViewModel都不会被清理掉。

        从这里看出来ViewModel对应key的唯一性

        ViewModel工作原理的核心技术点:

        观察者模式、工程模式、反射、Hashmap数据结构

        ViewModel在MVVM架构模型中,与DataBinding结合使用,会让你有起飞的感觉。后续会进一步加深使用。本篇仅以学会使用、了解原理为重点。

相关栏目:热点