乐趣区

关于android:Android应用架构MVVM模式

前言

随着 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 如有侵权,请分割删除。

退出移动版