Android开发中的架构

Android中的开发架构是用来描绘视图层逻辑层数据层三者之间的联系的。

  • 视图层:用户界面,即界面的展现和交互事情的呼应。
  • 逻辑层:为了完结体系功能而进行的必要逻辑。
  • 数据层:数据的获取和存储,含本地数据和服务器数据。

一般咱们的架构之路是从 MVC -> MVP -> MVVM 演化的。

MVC

MVC,Model-View-Controller,责任分类如下:

  • Model,模型层,即数据模型,用于获取和存储数据。
  • View,视图层,在Android中便是xml布局。
  • Controller,操控层,担任事务逻辑。

View接收到用户操作事情,告诉到Controller进行对应的逻辑处理,然后告诉Model去获取/更新数据,Model再把新的数据传递给View更新界面,这便是一个完整MVC的数据流向。

但在Android中,由于xml布局作为视图层能力很弱,很多操作View的代码是写在Activity/Fragment中的,而事务逻辑相同也是写在Activity/Fragment中。

Jetpack MVVM

所以,Android中MVC的问题点如下:

  1. Activity/Fragment的责任不明确,一起担任View和Controller,这样就导致Activity/Fragment的代码量很大,不满足单一责任准则。
  2. Model耦合View,View的修正会导致Controller和Model都进行改动,不满足最少常识准则。
MVP

MVP,Model-View-Presenter,责任分类如下:

  • Model,模型层,即数据模型,用于获取和存储数据。
  • View,视图层,即Activity/Fragment.
  • Presenter,操控层,担任事务逻辑。

MVP处理了MVC的问题:
1.Activity/Fragment的责任明确,逻辑不再写在Activity/Fragment中,而是在Presenter中;
2.Model不再持有View。

View层接收到用户操作事情后,调用Presenter进行逻辑处理,然后告诉Model获取或更新数据,Model把数据给到Presenter,Presenter再告诉View更新界面。

Jetpack MVVM

MVP的完结思路:

  • UI逻辑笼统成IView接口,由详细的Activity完结类来完结,且调用Presenter进行逻辑处理。
  • 事务逻辑笼统成IPresenter接口,由详细的Presenter完结类来完结。逻辑操作完结后调用IView接口办法改写UI。

MVP实质是面向接口编程,完结了依靠倒置准则。MVP处理了View层责任不明确的问题,但并没有处理代码耦合的问题,View和Presenter之间互相持有。

所以,MVP的问题点如下:

  1. 会引进大量的IView、IPresenter接口,增加完结的复杂度。
  2. View和Presenter互相持有,构成耦合。
MVVM

MVVM,Model-View-ViewModel,责任分类如下:

  • Model,模型层,即数据模型,用于获取和存储数据。
  • View,视图,即Activity/Fragment。
  • ViewModel,视图模型,担任事务逻辑。

注意,MVVM这儿的ViewModel便是一个名称,能够了解为MVP中的Presenter。不等同于前面文章中的ViewModel组件 ,Jetpack ViewModel组件是对MVVM的ViewModel的详细实施方案。

MVVM的实质是数据驱动,把解耦做的更完全,ViewModel不持有View 。

View产生事情,运用ViewModel进行逻辑处理后,告诉Model更新数据,Model把更新的数据给ViewModel,ViewModel自动告诉View更新界面而不是自动调用View的办法

Jetpack MVVM

到这儿你会发现,所谓的架构形式实质上了解很简单。比方MVP,甚至你都能够疏忽这个姓名,了解成在更高的层面上面向接口编程,完结了依靠倒置准则,便是这么简单。

MVVM的完结 —— Jetpack MVVM

Jetpack MVVM是MVVM架构在Android开发中的一种详细完结办法,是Google官方供给并引荐的。

Jetpack MVVM不仅经过数据驱动完结了View与ViewModel的解耦,还兼顾了Android页面开发中其他不可预期的过错,例如Lifecycle能妥善处理页面生命周期,防止View的空指针问题,ViewModel使得装备更改导致Activity重建时无需重新向后台恳求数据,节省了开销。

首要,请看下图,该图显现了一切模块应怎么互相交互:

Jetpack MVVM

各模块对应MVVM架构:

  • View层:Activity/Fragment
  • ViewModel层:Jetpack ViewModel + Jetpack LivaData
  • Model层:Repository库房,包含 本地耐久性数据 和 服务端数据

View层 包含了咱们平时写的Activity/Fragment/布局文件等与界面相关的东西。

ViewModel层 用于持有和UI相关的LiveData数据,以确保这些数据在屏幕旋转时不会丢掉,并且还要供给接口给View层调用以及和库房层进行通讯。

Model层 要做的主要作业是判别调用方恳求的数据应该从本地数据源中获取仍是从网络数据源中获取,并将获取到的数据回来给调用方。本地数据源能够运用数据库、SharedPreferences等耐久化技能来完结,而网络数据源则通常运用Retrofit访问服务器供给的Webservice接口来完结。

别的,图中一切的箭头都是单向的,例如View层指向了ViewModel层,表明View层会持有ViewModel层的引证,可是反过来ViewModel层却不能持有View层的引证。除此之外,引证也不能跨层持有,比方View层不能持有库房层的引证,谨记每一层的组件都只能与它相邻层的组件进行交互。

这种设计打造了共同且愉快的用户体验。不管用户前次运用运用是在几分钟前仍是几天之前,现在回到运用时都会当即看到运用在本地保留的数据。如果此数据已过期,则运用的Repository将开端在后台更新数据。

示例

这儿以一个获取气候信息并展现的事务来阐明怎么完结Jetpack MVVM。

新建一个气候信息类,里面有2个字段,一个字段用来表明什么样的气候,另一个表明温度:

public class WeatherInfo {
    //晴天/雨天/多云...
    private String text;
    //温度
    private String temp;
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
    public String getTemp() {
        return temp;
    }
    public void setTemp(String temp) {
        this.temp = temp;
    }
}

为了完结数据驱动,需求运用LiveData来包装气候信息,其间loadingLiveData是用来操控进度条的显现的。依据上面的架构图,还需求运用ViewModel来存储LiveData,这样在屏幕旋转时LiveData的数据不会丢掉:

public class WeatherInfoViewModel extends ViewModel {
    //气候信息
    private MutableLiveData<WeatherInfo> weatherInfoLiveData;
    //进条度的显现
    private MutableLiveData<Boolean> loadingLiveData;
    public LiveData<WeatherInfo> getWeatherInfoLiveData() {
        return weatherInfoLiveData;
    }
    public LiveData<Boolean> getLoadingLiveData() {
        return loadingLiveData;
    }
}

LiveData是一种可观察的数据存储器,运用组件(如 Activity、Fragment 和 Service)能够运用此存储器监控目标的更改,而无需在它们之间创建明确且严厉的依靠途径。LiveData组件还遵循运用组件的生命周期状况,并包含清理逻辑以防止目标走漏和过多的内存耗费。

注意到WeatherInfoViewModel类露出的getWeatherInfoLiveData()办法回来的是LiveData类型,即不可变的,而不是MutableLiveData,好处是防止数据在外部被更改。

咱们在WeatherActivity中观察LiveData的更新:

public class WeatherActivity extends AppCompatActivity{
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ...
        initData();
    }
    private void initData(){
        ViewModelProvider viewModelProvider = new ViewModelProvider(this);
        WeatherInfoViewModel weatherInfoViewModel = viewModelProvider.get(WeatherInfoViewModel.class);
        weatherInfoViewModel.getWeatherInfoLiveData().observe(this, new Observer<WeatherInfo>() {
            @Override
            public void onChanged(WeatherInfo weatherInfo) {
                //更新气候信息
                mTvWeather.setText(weatherInfo.getText());
                mTvTemp.setText(weatherInfo.getTemp());
            }
        });
        weatherInfoViewModel.getLoadingLiveData().observe(this, new Observer<Boolean>() {
            @Override
            public void onChanged(Boolean aBoolean) {
                //显现或躲藏加载进度条
                mProgressBar.setVisibility(aBoolean? View.VISIBLE:View.GONE);
            }
        });
        ...
    }
}

每次更新气候信息,相应的onChanged()办法都会回调,而不需求ViewModel自动调用View层办法更新UI,这便是数据驱动 —— 数据的更改驱动View自动更新。

由于LiveData具有生命周期感知能力,这意味着,只有Activity处于活跃状况,onChanged()才会回调。当Activity的onDestroy()执行时,LiveData会自动移除观察者。

鉴于WeatherInfoViewModel目标比对应的View目标存活的时间更长,WeatherInfoViewModel中不得包含对View目标的直接引证,包含Context。

咱们现已运用ViewModel将LiveData与Activity关联起来了,那么怎么获取气候信息呢?

一个简单的主意是直接在ViewModel中运用OkHttp从服务器获取气候信息,获取成功后将数据设置给LiveData,可是这样会使ViewModel承当了太多的责任,违反了单一责任准则。

在上面的架构图中,ViewModel将数据获取进程委派给一个新的模块——Repository。这儿咱们也照着架构图新建一个类WeatherRepository,用来从服务器获取气候信息:

public class WeatherRepository {
    private static WeatherRepository weatherRepository;
    public static WeatherRepository getWeatherRepository() {
        if(weatherRepository == null){
            weatherRepository = new WeatherRepository();
        }
        return weatherRepository;
    }
    public void getWeatherInfoFromServer(Callback callback){
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(15L, TimeUnit.SECONDS)
                .readTimeout(15L, TimeUnit.SECONDS)
                .writeTimeout(15L, TimeUnit.SECONDS)
                .retryOnConnectionFailure(true);
        HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger(){
            @Override
            public void log(String message) {
                Log.d("TAG", "url info:" + message);
            }
        });
        logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        builder.addInterceptor(logInterceptor);
        OkHttpClient okHttpClient = builder.build();
        Request request = new Request.Builder()
                .url("http://xxxx")
                .get()
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new okhttp3.Callback(){
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d("TAG", "onFailure: ");
                callback.onFailed(e.getMessage());
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    String data = response.body().string();
                    Gson gson = new Gson();
                    WeatherInfo weatherInfo;
                    try {
                        weatherInfo = gson.fromJson(data, WeatherInfo.class);
                        callback.onSuccess(weatherInfo);
                    } catch (Exception e){
                        Log.i("TAG", "Exception e:" + e.getMessage());
                    }
                }
            }
        });
    }
}

getWeatherInfoFromServer()办法传入一个接口回调Callback:

public interface Callback<T> {
    //成功的时分回调
    public void onSuccess(T t);
    //失败的时分回调
    public void onFailed(String msg);
}

在WeatherInfoViewModel中调用getWeatherInfoFromServer()办法,并在接口回调中修正LiveData中的值:

public class WeatherInfoViewModel extends ViewModel {
    //气候信息
    private MutableLiveData<WeatherInfo> weatherInfoLiveData;
    //进条度的显现
    private MutableLiveData<Boolean> loadingLiveData;
    public LiveData<WeatherInfo> getWeatherInfoLiveData() {
        return weatherInfoLiveData;
    }
    public MutableLiveData<Boolean> getLoadingLiveData() {
        return loadingLiveData;
    }
    public void getWeatherInfo(){
        //获取气候信息前显现进度条
        loadingLiveData.setValue(true);
        WeatherRepository.getWeatherRepository().getWeatherInfoFromServer(new Callback<WeatherInfo>() {
            @Override
            public void onSuccess(WeatherInfo weatherInfo) {
                //获取成功后,让进度条消失
                loadingLiveData.setValue(false);
                weatherInfoLiveData.setValue(weatherInfo);
            }
            @Override
            public void onFailed(String msg) {
                loadingLiveData.setValue(false);
                weatherInfoLiveData.setValue(null);
            }
        });
    }
}

这样就把获取气候信息委派给了WeatherRepository,当咱们还想从本地获取气候信息直接在WeatherRepository中增加相应的办法即可:

public class WeatherRepository {
    public void getWeatherInfoFromLocal(){
        //从本地获取气候信息
    }
    public void saveWeatherInfoToLocal(){
        //将气候信息存入本地
    }
    public void getWeatherInfoFromServer(){
        //从服务器获取气候信息
    }
}

最后,还要在Activity中,调用weatherInfoViewModel.getWeatherInfo()获取气候信息。这样,经过WeatherRepository获取到气候信息后,回调给ViewModel更新其间的LiveData的值,经过数据驱动,相应的View能够拿到气候信息更新UI。

以上便是经过Jetpack MVVM完结的MVVM架构的完结方案。