美文网首页android框架学习学习收藏
《安卓-深入浅出MVVM教程》应用篇-01Hello MVVM

《安卓-深入浅出MVVM教程》应用篇-01Hello MVVM

作者: IT天宇 | 来源:发表于2017-10-23 23:54 被阅读1527次

    简介

    背景

    这几年 MVP 架构在安卓界非常流行,几乎已经成为主流框架,它让业务逻辑 和 UI操作相对独立,使得代码结构更清晰。


    MVVM 在前端火得一塌糊涂,而在安卓这边却基本没见到几个人在用,看到介绍 MVVM 也最多是讲 DataBinding 或 介绍思想的。偶尔看到几篇提到应用的,还是对谷歌官网的Architecture Components 文章的翻译。

    相信大家看别人博客或官方文档的时候,总会碰到一些坑。要么入门教程写得太复杂(无力吐槽,前面写一堆原理,各种高大上的图,然并卵,到实践部分一笔带过,你确定真的是入门教程吗)。要么就是简单得就是一个 hello world,然后就没有下文了(看了想骂人)。


    实在看不下去的我,决定插手你的人生。

    目录

    《安卓-深入浅出MVVM教程》大致分两部分:应用篇、原理篇。
    采用循序渐进方式,内容深入浅出,符合人类学习规律,希望大家用最少时间掌握 MVVM。

    应用篇:

    01 Hello MVVM (快速入门)
    02 Repository (数据仓库)
    03 Cache (本地缓存)
    04 State Lcee (加载/空/错误/内容视图)
    05 Simple Data Source (简单的数据源)
    06 Load More (加载更多)
    07 DataBinding (数据与视图绑定)
    08 RxJava2
    09 Dragger2
    10 Abstract (抽象)
    11 Demo (例子)
    12-n 待定(欢迎 github 提建议)

    原理篇

    01 MyLiveData(最简单的LiveData)
    02-n 待定(并不是解读源码,那样太无聊了,打算带你从0撸一个 Architecture)

    关于提问

    本人水平和精力有限,如果有大佬发现哪里写错了或有好的建议,欢迎在本教程附带的 github仓库 提issue。
    What?为什么不在博客留言?考虑到国内转载基本无视版权的情况,一般来说你都不是在源出处看到这篇文章,所以留言我也一般是看不到的。

    教程附带代码

    https://github.com/ittianyu/MVVM

    应用篇放在 app 模块下,原理篇放在 implementation 模块下。
    每一节代码采用不同包名,相互独立。

    前言

    快一年没更新安卓技术栈了,今年年初就发现谷歌正在推 Architecture 。这是谷歌推出的快速实现 MVVM 架构的类库。目前已经是RC版本了,正式版应该不远了。体验完这么一个架构之后,只能用一个字形容。

    虽然不打算长久的做安卓开发,但在其位谋其职,考虑到安卓社区的发展,有必要给大家涨涨姿势。

    思想

    什么是 MVVM?

    很简单, Model(数据) View(视图) ViewModel(数据视图管理器)

    能不能具体点?

    Model:bean(实体类)、Data Source(Http请求相关、数据库相关)
    View:xml、View、Activity、Fragment 等UI相关
    ViewModel:这正是我要说的,先简单理解为管理器

    执行流程

    ViewModel 负责调用 Model(可以称之为数据源),拿到结果后,更新自身。而 View 与 ViewModel 双向绑定(后面会讲怎么实现绑定的),所以 View 就会自动更新。
    这就是 MVVM 大致的思想。

    环境配置

    首先需要引入 Lifecycles, LiveData and ViewModel 这三个全家桶,而这些库是放在谷歌 maven 上,所以你得在项目根 gradle 文件中加上谷歌 maven

    allprojects {
        repositories {
            jcenter()
            maven { url 'https://maven.google.com' }
        }
    }
    

    然后在 app 模块的 gradle 文件中加上如下 3 个库,为了便于后面正式版发布后修改版本号,我们通过引用变量。

    // Lifecycles, LiveData and ViewModel
    compile "android.arch.lifecycle:runtime:$rootProject.lifecycleRuntime"
    compile "android.arch.lifecycle:extensions:$rootProject.lifecycle"
    annotationProcessor "android.arch.lifecycle:compiler:$rootProject.lifecycle"
    

    相应的,你需要在根 gradle 文件中加上附加属性(gradle 的语法)

    ext {
        lifecycle = '1.0.0-rc1'
        lifecycleRuntime = '1.0.3'
    }
    

    Model

    为了降低学习难度,打算写一个展示用户 id 和 name 的demo。
    所以 bean 真的是简单得只有2个属性,省略了 get 和 set 方法(读者练习时自己加上)。

    public class User implements Serializable {
        private int id;
        private String name;
    // ...getter setter and toString...
    }
    

    Model 就写完了,what?说好的数据源呢?
    慌,不要慌!下次再讲。

    View

    所以相应的,我们定义 2 个 TextView 来展示 id 和 name。

    a_activity_user.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:orientation="vertical"
        android:id="@+id/v_root"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <TextView
            android:id="@+id/tv_id"
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
        <TextView
            android:id="@+id/tv_name"
            android:gravity="center"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
    </LinearLayout>
    

    然后在 Activity 中初始化并获得引用。
    这里需要注意,如果你的 support lib >= 26.0.0,则直接继承 AppCompatActivity 就好。否则需要继承 LifecycleActivity。

    public class UserActivity extends AppCompatActivity {
        private TextView tvId;
        private TextView tvName;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.a_activity_user);
            initView();
            initData();
        }
    
        private void initView() {
            tvId = (TextView) findViewById(R.id.tv_id);
            tvName = (TextView) findViewById(R.id.tv_name);
        }
    
        private void initData() {
            // todo
        }
    
    }
    
    

    Lifecycle

    上面提到 LifecycleActivity,实际上还有 LifecycleFragment,类似的,在 support lib >= 26.0.0 时,默认的 Activity 和 Fragment 基类里面已经集成了,不需要你做修改了。具体可以看官方文档 Lifecycle

    用于管理 Activity / Freagment 生命周期,方便查询当前组件生命周期的状态。对应的关系如下图。

    那到底有什么用?

    只要持有 Lifecycle,就等于知道了容器的生命周期。
    比如,实现 LifecycleObserver 之后,就可以在状态发生改变时及时响应。也就是等同于让 Model或其他也拥有了生命周期。(还有这种操作?)

    定义一个定位监听器,可以在生命周期不同状态下,做对应的操作,再也不用在 Activity 中加生命周期注册或取消操作了。

    class MyLocationListener implements LifecycleObserver {
        private boolean enabled = false;
        public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) {
           ...
        }
    
        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        void start() {
            if (enabled) {
               // connect
            }
        }
    
        public void enable() {
            enabled = true;
            if (lifecycle.getState().isAtLeast(STARTED)) {
                // connect if not connected
            }
        }
    
        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        void stop() {
            // disconnect if connected
        }
    }
    

    而在 Activity 中,只需要传入 lifecycle 就可以了。优雅得不像话。

    myLocationListener = new MyLocationListener(this, getLifecycle(), location -> {
                // update UI
            });
    

    ViewModel

    ViewModel 就类似于 MVP 中的 Presenter 或者 MVC 中的 Controller。(不清楚什么是 MVC MVP 也没关系,就是个概念而已)

    现在我们完全没有必要自己定义 ViewModel 来管理 UI 生命周期,谷歌大礼包中就有 ViewModel

    所以很自然的我们写出这样的一个类。

    public class UserViewModel extends ViewModel {}
    

    你可能好奇 ViewModel,点进去看发现就是只有一个空方法的抽象类。可以,这很谷歌。但从 onCleared 这方法名可以看出,这还是有用处的。

    LiveData

    接下来就是要去调用 Model 并和 View 绑定,在继续往下讲之前,还需要介绍一下 LiveData,没错,就是它,可以轻松的实现和 View 的绑定。

    它能自动响应生命周期方法,可被观察(数据改变时通知观察者),所以可以实现 Model 和 View 的绑定。

    具有以下优点:

    • 避免内存泄露:由于 Observer 和 Lifecycle 绑定,当 Lifecycle 被销毁后,Observer 自动被清理。
    • 避免崩溃:避免在 Activity 被销毁后更新数据导致的崩溃情况
    • 数据可共享:数据可在多个 fragment 中共享。甚至可以通过单例的 LiveData 实现全局数据共享
    • 不需要在 Activity 中处理生命周期
    • 数据更新及时:当数据在 UI 不可见的时更新了,在可见时,数据会及时更新到UI上

    具体用法

    创建一个可变的 LiveData,也就是 MutableLiveData。
    并通过一个方法返回。没错,就是这么简单,只需要 new 出来就行了。

    // Create a LiveData with a String
    private MutableLiveData<String> mCurrentName;
    
    public MutableLiveData<String> getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = new MutableLiveData<String>();
        }
        return mCurrentName;
    }
    
    

    在 Activity / Fragment 中 观察 LiveData。也就是当 LiveData 数据发生变化时,自动回调方法来更新 UI。

    final Observer<String> nameObserver = new Observer<String>() {
        @Override
        public void onChanged(@Nullable final String newName) {
            // Update the UI, in this case, a TextView.
            mNameTextView.setText(newName);
        }
    };
    getCurrentName().observe(this, nameObserver);
    
    

    那什么时候会更新数据?

    更新数据一般是 UI 这边触发的,比如我们给按钮绑定一个事件,点击之后去更改 name。

    mButton.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            String anotherName = "John Doe";
            getCurrentName().setValue(anotherName);
        }
    });
    

    是的,你想更新 UI 上的文本,只需要修改 LiveData 中的数据即可。

    ViewModel + LiveData

    讲完 LiveData,我们回到 ViewModel。
    为了显示 id 和 name,我们需要一个 LiveData<User>。
    对外公开一个 getUser 方法 和 setUsername 方法。
    这里你会发现 getUser 并不是线程安全的,因为这个案例不会有在多线程中 getUser,所以没必要加,实际上还可以把 user 初始化放到成员变量定义的地方。

    public class UserViewModel extends ViewModel {
        private MutableLiveData<User> user;
    
        public LiveData<User> getUser() {
            if (user == null)
                user = new MutableLiveData<>();
            return user;
        }
    
        public void setUsername(String username) {
            user.setValue(new User(1, username));
        }
    }
    

    View 和 ViewModel 绑定

    之前提到了 LiveData,它可以方便的被观察。
    还有 ViewModel,可以自动响应生命周期。
    所以就是这两个东西,方便的实现绑定操作。

    之前我已经初始化了 UserActivity 的View,现在来完善 initData。

    通常来说,一个 View 只和一个 ViewModel 绑定。所以我们需要一个 Activity 级别的单例 ViewModel,我们可以通过 ViewModelProviders.of(this).get(UserViewModel.class) 来简单的实现。

    然后通过 userViewModel 取得 LiveData 并添加监听(绑定操作)。

    这个回调方法里面,我们就可以更新与该数据相关的 UI。

    没错,绑定就是这么简单便捷。

        private UserViewModel userViewModel;
    
        private void initData() {
            userViewModel = ViewModelProviders.of(this).get(UserViewModel.class);
            userViewModel.getUser().observe(this, new Observer<User>() {
                @Override
                public void onChanged(@Nullable User user) {
                    updateView(user);
                }
            });
    
            userViewModel.setUsername("ittianyu");
        }
    
        private void updateView(User user) {
            tvId.setText(user.getId() + "");
            tvName.setText(user.getName());
        }
    

    总结

    有了这样一个构架,以后就不需要为 n 处都要修改 View 导致的复杂的 UI 逻辑而头疼了。

    而数据请求也可以更纯粹,不需要管 UI 是怎么显示的。

    简单来说就是 请求数据完成并更新数据后,通过通知 UI 去根据数据来渲染界面。

    没错,今后安卓也可以像前端那样愉快的写 UI 了。也可以像后端那样层次分明的专心处理数据了,妙不可言。

    相关文章

      网友评论

        本文标题:《安卓-深入浅出MVVM教程》应用篇-01Hello MVVM

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