还在用 MVP?快来试试 MVVM框架吧!

作者: df556ada620a | 来源:发表于2018-12-31 17:33 被阅读18次

    优势

    稳定

    • 减少内存泄漏:新手很容易在线程切换的地方写出导致内存泄漏的代码,但如果把线程切换交给框架来做,出错的概率就大大降低。
    • 减少 crash:根据我的开发经历,大部分 crash 都是空指针导致的。一般线程回调里最容易出现问题,当UI销毁后,子线程依旧去操作UI,容易导致 crash。 本框架有完善的生命周期,UI销毁后,框架对子线程做了强制的停止操作,大大减少 crash 的概率。

    轻量

    提示:这两个依赖库在 Android Studio 新建的项目里几乎都包含,也就是几乎 0 依赖。

    接入成本低

    • 侵入性低:不需要修改任何现有代码
    • 无缝嵌入:可间接当做 View 使用,无论之前使用 MVP 还是 MVC,往里面加一个 View 根本不影响你的结构。

    简单

    • 对原生开发友好:你几乎不需要学习框架 api 就可以开始使用。
    • 熟悉 react 和 flutter 的非常容易上手

    解耦

    MVVM 的强大之处在于 UI 和 逻辑 分离,处理逻辑时不需要关心 UI,写 UI 时不需要管数据从哪获取。

    要更新时,你直接对数据进行修改,就会自动触发重新渲染。 并不需要担心性能问题,因为默认情况下,原来的 View 并不会被抛弃掉,仅仅会触发一次 update 操作。

    public class StatefulUserWidget extends StatefulWidget<View, UserWidget> {
        private UserBean user = UserDataSource.getInstance().getUser();
    
        public StatefulUserWidget(Context context, Lifecycle lifecycle) {
            super(context, lifecycle);
        }
    
        @Override
        protected State<UserWidget> createState(Context context) {
            return StateUtils.create(new UserWidget(context, lifecycle, user));
        }
    
        @Override
        public void initWidget(UserWidget widget) {
            widget.setOnClickListener(v -> setState(() -> {
                user = UserDataSource.getInstance().getUser();
            }));
            update();
        }
    
        @Override
        public void update() {
            super.update();
            widget.setUser(user);
        }
    }
    
    

    initWidget 方法中对 widget 设置了一个点击事件,点击后重新获取数据,自动触发 UI 的更新。 其实就是调用了 setState 方法来触发更新,类似于 reactflutter,更新数据的操作需要放到该方法中,否则不会触发更新。

    高复用

    本框架的设计思想类似于 flutter 的 "Everything's a Widget",即把所有的东西都视为控件。 各个控件之间保持独立,容器控件可以组合一个或多个控件,每个控件都有独立的生命周期。 因此,控件的复用性大大提高。

    便捷的生命周期

    得益于谷歌新引进的 lifecycle,让每个 widget 都可以拥有完整的生命周期,甚至数据也可以拥有生命周期。

    异步支持 (同步发请求)

    对于客户端编程来说,最麻烦的是各种异步调用和状态同步。 多线程编程很难,稍有不慎,轻则内存泄漏,重则直接蹦溃。 本框架内部做了处理异步请求,并在 onDestroy 时,自动取消子线程的操作,防止内存泄漏 或者 异步导致的空指针问题。

    本库提供了如下方法支持数据修改,各位开发者可自行选择合适的方法。

    • setState:同步执行数据修改操作(适用于非耗时的数据修改操作,无线程切换性能消耗)
    • setStateAsync:异步执行的数据修改操作,并在UI销毁时自动停止异步线程
    • setStateAsyncWithCache:类似于 setStateAsync ,对缓存提供支持。

    有了它,你可以同步的方式去发网络请求。 合并多个请求的数据变得异常轻松(比如 先请求a,在请求b,合并结果变成c)。

    缓存支持

    生活 缓存很难。 一千个应用有一千种缓存。 我见过网上很多缓存方案非常粗糙,大部分是直接在网络层通过拦截器来做。 因为这样不用侵入到业务代码。 但是,这样做的弊端也很大,不够灵活。 虽然像 okhttp 这样的网络库提供了对缓存的支持,比如可以设置只使用缓存,或者只使用网络,但这依然不够灵活。

    如果想精准控制缓存,那就不得不自己在代码里为每一个请求都加上缓存的逻辑。 你会发现这就导致相同的缓存逻辑写了无数遍,这简直是噩梦。

    不过因为本库有异步支持,所以处理缓存也变得简单多了。 至于你想怎么使用缓存,交给你自己判断吧,我们提供了一个策略接口,你只需要实现它即可。

    页面状态管理

    无数据页面、 错误页面、 加载中页面、 下拉刷新、 加载更多 在应用中很常见。

    实现起来却不方便了,常见的做法是 BaseActivity BaseFragment,但我表示不希望看见它们,曾今我觉得 base 是很好的逻辑抽象和封装,后来发现自从有了 base,迁移和复用几乎变成了 0。 base 使得它们紧紧的耦合在一起。 如果你不明白我在说什么,我给你举个例子:

    我想从项目 A 中抽出一个页面和逻辑差不多的 Activity,以便于在项目 B 中使用,这个时候最常见的就是 复制 XxxActivity.java 到 B 项目,然后后面你懂的。

    但本库对这几种页面状态提供了高度的封装,你不必再依赖于 Base。 不仅仅是 activity,甚至一个 button,你都可以让他拥有如上的这几种状态。

    请求过滤

    不知道你是否烦恼过,产品跟你说,用户可能狂按按钮,让你加个判断,减少不必要的请求。 听起来需求很简单,防止重复点击就行,但可达鸭眉头一皱,发现事情并不简单。 一个按钮防重复点击也就几行代码,但几十个几百个按钮呢? 你说可以抽出一个 BaseButton? 那点击的如果是个 text 或 fab 这样的控件呢? 确实 base 可以解决很多重复代码,但相应的你要把对应的控件全部换成 base,工作量也很大。

    本库贴心的为大家提供了请求过滤器,默认就过滤重复的请求,虽然不是在 UI 上过滤,但同一个 task 的请求是不会重复执行的,这点可以放心。 如果你有其他过滤需求,还可以自定义实现一个过滤器。

    重试支持

    请求失败重试也是很常见的需求,但实现并不简单,基本有2种做法:

    • 如果在代码层面做,就需要在请求失败的回调里重新发起一次,还要记录次数,很是麻烦。
    • 如果在网络层做,你就得对网络层进行一次封装,提供一个方法设置重试次数。 然而,这种方法弊端很大,不能和业务很好的联系。 因为网络层并不知道什么时候应该重试,网络请求失败就重试? 还是返回内容里面标识不成功就重试呢?

    本库同样提供了重试支持,因为有了异步支持,重试对框架来说,就是一个循环,然而这个循环框架都帮你写好了,你只需告诉框架重试次数和什么时候应该重试就可以了。

    动态属性设置能力

    动态换肤或样式修改也是一个很常见的需求,然而为了实现这样的需求,往往需要开发者在代码里提前写好根据配置修改的UI的代码。

    本库同样提供了支持,你可以通过一个 json 来对 wiget 进行属性修改。 所以换个皮肤或改个样式都是分分钟的事啦。

    单页面应用(测试)

    搞前端的应该很清楚这是什么,就是所有渲染都是在一个页面上展示,页面跳转都是通过前端路由来控制。 对应到客户端,就是所有 UI 都在一个 activity 中展示。

    这样做有什么意义? 安卓插件化最大的问题是四大组件需要提前在 manifest 中注册,虽然目前有一些开源项目通过底层 hook 方式解决了这个问题,但是以后的安卓版本就不清楚会不会把这个限制了。 而且目前的插件化都需要对资源进行合并,这就使得成功率下降。

    如果是单页面应用,动态下发字节码执行也变得有可能。 而且这样成功率理论上接近 100%。 打算有时间尝试一下。

    我的意思就是像前端那样具有随时更新的能力,不知道会不会被封杀,逃。。。

    快速开始

    引入库

    【可选】 添加 java8 支持

    android {
    ...
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    ...
    }
    
    

    添加 maven 仓库

    allprojects {
        repositories {
            ...
            maven { url 'https://jitpack.io' }
        }
    }
    
    

    添加依赖

    def support_version = '28.0.0'
    def lifecycle_version = '1.1.1'
    
    implementation 'com.github.ittianyu:relight:0.1.0'
    implementation "com.android.support:appcompat-v7:$support_version"
    implementation "com.android.support:design:$support_version"
    
    // Support library depends on this lightweight import
    implementation "android.arch.lifecycle:runtime:$lifecycle_version"
    
    

    如果开启了 java8

    // alternately - if using Java8, use the following instead of compiler
    implementation "android.arch.lifecycle:common-java8:$lifecycle_version"
    
    

    混淆

    使用了 xml 支持,必须加入混淆,未使用的可以不加。

    -keep class * extends com.ittianyu.relight.widget.Widget {*;}
    
    

    小结;

    后面会写一篇mvvp框架实战,以及它的具体使用。也欢迎大家加入Android进阶交流群;964557053。进群可免费领取一份最新技术大纲和Android进阶资料。请备注简书

    相关文章

      网友评论

        本文标题:还在用 MVP?快来试试 MVVM框架吧!

        本文链接:https://www.haomeiwen.com/subject/ydlwlqtx.html