前言

随着Android利用开发技术的一直倒退和成熟,很开发者越来越关注着Android利用架构的设计。目前,Android的利用架构次要有MVC、MVP和MVVM模式,咱们就来说一下MVVM模式。

MVP模式

MVVM模式能够说是MVP模式的进一步倒退,所以先来理解一下MVP模式。

MVP (Model-View-Presenter) 模式的构造如下图所示:

MVP模式将利用分为三层:Model层次要负责数据的提供,View层次要负责界面的显示,Presenter层次要负责业务逻辑的解决。

在MVP模式中,Model层和View层不能间接通信,Presenter层负责充当中间人,实现Model层和View层之间的间接通信。View层和Presenter层相互持有对方的援用,实现View层和Presenter层之间的通信。

MVP模式的次要长处是:拆散了Model层和View层,拆散了视图操作和业务逻辑,升高了耦合。

MVVM模式

MVVM (Model-View-ViewModel) 模式的构造如下图所示:

MVVM模式与MVP模式一样,也将利用分为三层,并且各个对应的层的职责类似:

  • Model层,次要负责数据的提供。Model层提供业务逻辑的数据结构(比方,实体类),提供数据的获取(比方,从本地数据库或者近程网络获取数据),提供数据的存储。
  • View层,次要负责界面的显示。View层不波及任何的业务逻辑解决,它持有ViewModel层的援用,当须要进行业务逻辑解决时告诉ViewModel层。
  • ViewModel层,次要负责业务逻辑的解决。ViewModel层不波及任何的视图操作。通过官网提供的Data Binding库,View层和ViewModel层中的数据能够实现绑定,ViewModel层中数据的变动能够主动告诉View层进行更新,因而ViewModel层不须要持有View层的援用。ViewModel层能够看作是View层的数据模型和Presenter层的联合。

MVVM模式与MVP模式最大的区别在于:ViewModel层不持有View层的援用。这样进一步升高了耦合,View层代码的扭转不会影响到ViewModel层。

MVVM模式绝对于MVP模式次要有如下长处:

  • 进一步升高了耦合。ViewModel层不持有View层的援用,当View层产生扭转时,只有View层绑定的数据不变,那么ViewModel层就不须要扭转。而在MVP模式下,当View层产生扭转时,操作视图的接口就要进行相应的扭转,那么Presenter层就须要批改了。
  • 不必再编写很多样板代码。通过官网的Data Binding库,UI和数据之间能够实现绑定,不必再编写大量的findViewById()和操作视图的代码了。总之,Activity/Fragment的代码能够做到相当简洁。

例子

上面举一个简略的例子来实际MVVM模式。残缺的我的项目代码能够去GitHub上查看:

https://github.com/chongyucaiyan/MVVMDemo

例子实现的次要性能是:点击按钮网络查问天气,查问胜利后在界面上显示天气信息。主界面如下图所示:

MVVM模式的代码组织构造倡议依照 业务性能 进行划分,具体操作是:每个业务性能独立一个包寄存,每个业务性能包上面再按Model、View、ViewModel分包寄存。所有的Model寄存在model包上面,所有的Activity和Fragment寄存在activity包上面,所有的ViewModel寄存在viewmodel包上面。该例子比较简单,只有一个weather业务功能模块,最终的代码组织构造如下图所示:


编写Model

查问杭州天气的URL为:

http://www.weather.com.cn/data/cityinfo/101210101.html

拜访该URL将返回一串JSON字符串,如下所示:

{"weatherinfo":{"city":"杭州","cityid":"101210101","temp1":"5℃","temp2":"20℃","weather":"晴转多云","img1":"n0.gif","img2":"d1.gif","ptime":"18:00"}}

依照此JSON字符串,能够编写相应的实体类。WeatherData类的代码如下所示:

public class WeatherData {    private WeatherInfo weatherinfo;    public WeatherInfo getWeatherinfo() {        return weatherinfo;    }    public void setWeatherinfo(WeatherInfo weatherinfo) {        this.weatherinfo = weatherinfo;    }}

WeatherInfo类的代码如下所示:

public class WeatherInfo {    private String city;    private String cityid;    private String temp1;    private String temp2;    private String weather;    private String img1;    private String img2;    private String ptime;    public String getCity() {        return city;    }    public void setCity(String city) {        this.city = city;    }    public String getCityid() {        return cityid;    }    public void setCityid(String cityid) {        this.cityid = cityid;    }    public String getTemp1() {        return temp1;    }    public void setTemp1(String temp1) {        this.temp1 = temp1;    }    public String getTemp2() {        return temp2;    }    public void setTemp2(String temp2) {        this.temp2 = temp2;    }    public String getWeather() {        return weather;    }    public void setWeather(String weather) {        this.weather = weather;    }    public String getImg1() {        return img1;    }    public void setImg1(String img1) {        this.img1 = img1;    }    public String getImg2() {        return img2;    }    public void setImg2(String img2) {        this.img2 = img2;    }    public String getPtime() {        return ptime;    }    public void setPtime(String ptime) {        this.ptime = ptime;    }}

编写ViewModel

ViewModel不波及任何的视图操作,只进行业务逻辑的解决。通过官网提供的Data Binding库,当ViewModel中的数据发生变化时,UI将自动更新。QueryWeatherViewModel的代码如下所示:

public class QueryWeatherViewModel {    private static final String TAG = "QueryWeatherViewModel";    public final ObservableBoolean loading = new ObservableBoolean(false);    public final ObservableBoolean loadingSuccess = new ObservableBoolean(false);    public final ObservableBoolean loadingFailure = new ObservableBoolean(false);    public final ObservableField<String> city = new ObservableField<>();    public final ObservableField<String> cityId = new ObservableField<>();    public final ObservableField<String> temp1 = new ObservableField<>();    public final ObservableField<String> temp2 = new ObservableField<>();    public final ObservableField<String> weather = new ObservableField<>();    public final ObservableField<String> time = new ObservableField<>();    private Call<WeatherData> mCall;    public QueryWeatherViewModel() {    }    public void queryWeather() {        loading.set(true);        loadingSuccess.set(false);        loadingFailure.set(false);        mCall = RetrofitManager.get()                .create(QueryWeatherRequest.class)                .queryWeather();        mCall.enqueue(new Callback<WeatherData>() {            @Override            public void onResponse(Call<WeatherData> call, Response<WeatherData> response) {                WeatherInfo weatherInfo = response.body().getWeatherinfo();                city.set(weatherInfo.getCity());                cityId.set(weatherInfo.getCityid());                temp1.set(weatherInfo.getTemp1());                temp2.set(weatherInfo.getTemp2());                weather.set(weatherInfo.getWeather());                time.set(weatherInfo.getPtime());                loading.set(false);                loadingSuccess.set(true);            }            @Override            public void onFailure(Call<WeatherData> call, Throwable t) {                if (call.isCanceled()) {                    Log.i(TAG, "call is canceled.");                } else {                    loading.set(false);                    loadingFailure.set(true);                }            }        });    }    public void cancelRequest() {        if (mCall != null) {            mCall.cancel();        }    }}

编写View

View不波及任何的业务逻辑解决,只进行界面的显示。在xml布局文件中,通过官网提供的Data Binding库,将UI与ViewModel中的数据进行绑定,当ViewModel中的数据发生变化时,UI将自动更新。xml布局文件的代码如下所示:

<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools">    <data>        <import type="android.view.View" />        <variable            name="viewModel"            type="com.github.cyc.mvvmdemo.weather.viewmodel.QueryWeatherViewModel" />    </data>    <RelativeLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:padding="@dimen/default_content_padding"        tools:context="com.github.cyc.mvvmdemo.weather.activity.QueryWeatherActivity">        <Button            android:id="@+id/btn_query_weather"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerHorizontal="true"            android:text="@string/query_weather"            android:enabled="@{viewModel.loading ? false : true}"            android:onClick="@{() -> viewModel.queryWeather()}" />        <RelativeLayout            android:id="@+id/vg_weather_info"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:layout_below="@id/btn_query_weather"            android:layout_marginTop="@dimen/query_weather_margin"            android:visibility="@{viewModel.loadingSuccess ? View.VISIBLE : View.GONE}">            <TextView                android:id="@+id/tv_city"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:textStyle="bold"                android:text="@string/city" />            <TextView                android:id="@+id/tv_city_value"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_toRightOf="@id/tv_city"                android:layout_alignBottom="@id/tv_city"                android:text="@{viewModel.city}"                tools:text="杭州" />            <TextView                android:id="@+id/tv_city_id"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_below="@id/tv_city"                android:layout_marginTop="@dimen/query_weather_margin"                android:textStyle="bold"                android:text="@string/city_id" />            <TextView                android:id="@+id/tv_city_id_value"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_toRightOf="@id/tv_city_id"                android:layout_alignBottom="@id/tv_city_id"                android:text="@{viewModel.cityId}"                tools:text="101210101" />            <TextView                android:id="@+id/tv_temp"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_below="@id/tv_city_id"                android:layout_marginTop="@dimen/query_weather_margin"                android:textStyle="bold"                android:text="@string/temperature" />            <TextView                android:id="@+id/tv_temp1_value"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_toRightOf="@id/tv_temp"                android:layout_alignBottom="@id/tv_temp"                android:text="@{viewModel.temp1}"                tools:text="5℃" />            <TextView                android:id="@+id/tv_tilde"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_toRightOf="@id/tv_temp1_value"                android:layout_alignBottom="@id/tv_temp"                android:text="@string/tilde" />            <TextView                android:id="@+id/tv_temp2_value"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_toRightOf="@id/tv_tilde"                android:layout_alignBottom="@id/tv_temp"                android:text="@{viewModel.temp2}"                tools:text="10℃" />            <TextView                android:id="@+id/tv_weather"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_below="@id/tv_temp"                android:layout_marginTop="@dimen/query_weather_margin"                android:textStyle="bold"                android:text="@string/weather" />            <TextView                android:id="@+id/tv_weather_value"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_toRightOf="@id/tv_weather"                android:layout_alignBottom="@id/tv_weather"                android:text="@{viewModel.weather}"                tools:text="晴" />            <TextView                android:id="@+id/tv_time"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_below="@id/tv_weather"                android:layout_marginTop="@dimen/query_weather_margin"                android:textStyle="bold"                android:text="@string/release_time" />            <TextView                android:id="@+id/tv_time_value"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_toRightOf="@id/tv_time"                android:layout_alignBottom="@id/tv_time"                android:text="@{viewModel.time}"                tools:text="10:00" />        </RelativeLayout>        <ProgressBar            android:id="@+id/pb_progress"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:visibility="@{viewModel.loading ? View.VISIBLE : View.GONE}" />        <TextView            android:id="@+id/tv_query_failure"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:text="@string/query_failure"            android:visibility="@{viewModel.loadingFailure ? View.VISIBLE : View.GONE}" />    </RelativeLayout></layout>

在Activity中,通过官网提供的Data Binding库加载布局文件,创立ViewModel,并绑定View和ViewModel。QueryWeatherActivity的代码如下所示:

public class QueryWeatherActivity extends AppCompatActivity {    // ViewModel    private QueryWeatherViewModel mViewModel;    // DataBinding    private ActivityQueryWeatherBinding mDataBinding;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mDataBinding = DataBindingUtil.setContentView(this, R.layout.activity_query_weather);        // 创立ViewModel        mViewModel = new QueryWeatherViewModel();        // 绑定View和ViewModel        mDataBinding.setViewModel(mViewModel);    }    @Override    protected void onDestroy() {        super.onDestroy();        // 勾销申请        mViewModel.cancelRequest();    }}

总结

MVVM模式有三层:Model层次要负责数据的提供,View层次要负责界面的显示,ViewModel层次要负责业务逻辑的解决。各个层职责繁多不同,但他们都构造清晰,利用起来非常的便捷

Android零根底系列教程:Android根底课程

本文转自 Android | Android利用架构之MVVM模式_cyc的专栏-CSDN博客_android mvvm如有侵权,请分割删除。