1. 程式人生 > >Android官方MVVM框架實現組件化之整體結構

Android官方MVVM框架實現組件化之整體結構

並且 pat 功能 模塊 txt cti stat 開發人員 extends

一、google官方MVVM框架講解

我前面對比了MVC和MVP《兩張圖看懂Android開發中MVC與MVP的區別》,可以相對於MVC我們的MVP是有多優越,但是Android開發現在已經開始流行了MVVM,前不久google官方發布了MVVM的正式庫。官方的正式MVVM庫主要包括下面四個:

其中只有ViewModel是MVVM結構中的一個組件,其他的三個都是輔助性質的。

lifecycles 就是處理UI界面的生命周期,在26版本以後的Support庫中,AppCompatActivity和SupportActivity中都實現了LifecycleOwner,內部已經對UI界面的生命周期做了處理了。

LiveData是一個抽象類,我們可以存放UI頁面需要的數據,就是把數據包裝在LiveData中了,我們可以觀測LiveData中的數據變化,但是LiveData是跟UI的生命周期關聯的,當UI頁面銷毀了,LiveData的數據變化回調是不會執行的。

Room 就是一個sqlite數據持久化庫,我們也可以使用別的ORM庫。

二、MVVM架構優勢

《兩張圖看懂Android開發中MVC與MVP的區別》 前面兩張圖真是了MVC和MVP的區別,我這裏也來一張圖看看MVVM:

看上圖Model和View是不會發生關系的,ViewModel是把View和Model關聯起來的加工廠:

MVVM優勢總結:

1,View和Model雙向綁定,一方的改變都會影響另一方,開發者不用再去手動修改UI的數據。額,互相自動的。

2,不需要findViewById也不需要butterknife,不需要拿到具體的View去設置數據綁定監聽器等等,這些都可以用DataBinding完成。是不是很舒服?

3,View和Model的雙向綁定是支持生命周期檢測的,不會擔心頁面銷毀了還有回調發生,這個由lifeCycle完成。

4,不會像MVC一樣導致Activity中代碼量巨大,也不會像MVP一樣出現大量的View和Presenter接口。項目結構更加低耦合。

5,更低的耦合把各個模塊分開開發,分開測試,可以分給不同的開發人員來完成。

三、MVVM組件化示例項目架構分析

下圖是項目模塊和工程之間的依賴關系:

下圖是工程Android Studio中的目錄結構:

3.1 各模塊和彼此之間的關系解釋:

lib_opensource :第三方build.gradle依賴,本項目主要有support、lifecycle、room、fresco、retrofit、okhttp、RxJava、ARouter這些。

libcoremodel: 存放MVVM中的Model和ViewModel兩個模塊,就是數據的處理和數據與UI頁面的綁定。依賴libopensource庫。

libcommon : 公共庫,主要有各種base,各種ui組件,自定義組件,公用的Activity、公用的Fragment,和公用的utils等等。依賴libcoremodel庫。

module_girls : 妹子功能模塊,可以在library和application之間切換,自己可以是一個app也可以成為別的app的一個組件模塊。組件化編譯時為app,反之為module。

module_news : 新聞功能模塊,可以在library和application之間切換,自己可以是一個app也可以成為別的app的一個組件模塊。組件化編譯時為app,反之為module。

appuniversal : 定制版本的app,組件化編譯時 modulegirls和modulenews為app,所以不能把這兩個作為module加進來編譯,所以組件化編譯時appuniversal要依賴libcommon庫,反之就可以把 modulegirls和module_news作為module加進來編譯。

appspecific : 定制版本的app,組件化編譯時 modulegirls和modulenews為app,所以不能把這兩個作為module加進來編譯,所以組件化編譯時appspecific要依賴libcommon庫,反之就可以把 modulegirls和module_news作為module加進來編譯。

3.2 ARouter串聯各個模塊

使用ARouter來跳轉Activity和獲取Fragment,記得看之前別人的組件化結構文章,一直都在糾結Fragment的獲取問題,我想說的是有了ARouter來獲取Fragment不是超級簡單麽?

ARouter典型應用

從外部URL映射到內部頁面,以及參數傳遞與解析
跨模塊頁面跳轉,模塊間解耦
攔截跳轉過程,處理登陸、埋點等邏輯
跨模塊API調用,通過控制反轉來做組件解耦
3.3 組件化編譯和非組件化編譯切換

我們在工程根目錄下的gradle.properties文件中加入一個Boolean類型的變量,通過修改這個變量來識別編譯模式:

每次更改“isModule”的值後,需要點擊 "Sync Project" 按鈕

isModule是“集成開發模式”和“組件開發模式”的切換開關

isModule=false
然後在 modulegirls和modulenews中的build.gradle文件中支持切換:

if (isModule.toBoolean()) {
//組件化編譯時為application
apply plugin: ‘com.android.application‘
} else {
//非組件化編譯時為library
apply plugin: ‘com.android.library‘
}

android {
compileSdkVersion build_versions.target_sdk
buildToolsVersion build_versions.build_tools

defaultConfig {
    minSdkVersion build_versions.min_sdk
    targetSdkVersion build_versions.target_sdk
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

    //ARouter
    javaCompileOptions {
        annotationProcessorOptions {
            arguments = [moduleName: project.getName()]
        }
    }
}

buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile(‘proguard-android.txt‘), ‘proguard-rules.pro‘
    }
}
dataBinding {
    enabled = true
}
lintOptions {
    abortOnError false
}
sourceSets {
    main {
        if (isModule.toBoolean()) {
            //組件化編譯時為app,在對應的AndroidManifest文件中需要寫ndroid.intent.action.MAIN入口Activity
            manifest.srcFile ‘src/main/module/AndroidManifest.xml‘
        } else {
            manifest.srcFile ‘src/main/AndroidManifest.xml‘
            //集成開發模式下排除debug文件夾中的所有Java文件
            java {
                //debug文件夾中放的是Application類,非組件化時不用有此類
                exclude ‘debug/**‘
            }
        }
    }
}

}

dependencies {
implementation fileTree(dir: ‘libs‘, include: [‘*.jar‘])
api project(‘:lib_coremodel‘)
api project(‘:lib_common‘)
implementation ‘com.android.support:support-v4:26.1.0‘
annotationProcessor deps.arouter.compiler
}
上面看到了組件化和非組件化編譯會有不用的AndroidManifest文件,組件化時需要debug文件夾下面的application類,非組件化時排除此文件夾。

module下的AndroidManifest文件是組件化app編譯時的,寫了MAIN入口Activity

dubug下是組件化app編譯時的Application類,初始化作為一個app運行時需要的資源等等。在非組件化編譯在build.gradle文件中排除debug文件夾的所以東西。

3.4 最後預告:

後面會有一些列介紹在MVVM組件化過程中使用ARouter來跳轉Activity和獲取Fragment、DataBinding實現數據和UI的互相綁定、Rxjava2和Retrofit2動態數據獲取,和AndroidViewModel的封裝。

下面貼貼一個lib_coremodel庫中我封裝的AndroidViewModel,用泛型來確定數據類型,並且是動態URL獲取數據:

package google.architecture.coremodel.viewmodel;

import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.databinding.ObservableField;
import android.support.annotation.NonNull;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;

import google.architecture.coremodel.datamodel.http.ApiClient;
import google.architecture.coremodel.datamodel.http.ApiConstants;
import google.architecture.coremodel.datamodel.http.service.DynamicApiService;
import google.architecture.coremodel.util.JsonUtil;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.ResponseBody;

/**

  • Created by dxx on 2017/11/20.
    */

public class BaseViewModel<T> extends AndroidViewModel {

//生命周期觀察的數據
private MutableLiveData<T>  liveObservableData = new MutableLiveData<>();
//UI使用可觀察的數據 ObservableField是一個包裝類
public ObservableField<T> uiObservableData = new ObservableField<>();

private final CompositeDisposable mDisposable = new CompositeDisposable();

private static final MutableLiveData ABSENT = new MutableLiveData();
{
    //noinspection unchecked
    ABSENT.setValue(null);
}

public BaseViewModel(@NonNull Application application, String fullUrl) {
    super(application);
    ApiClient.initService(ApiConstants.GankHost, DynamicApiService.class).getDynamicData(fullUrl).subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<ResponseBody>() {
        @Override
        public void onSubscribe(Disposable d) {
            mDisposable.add(d);
        }

        @Override
        public void onNext(ResponseBody value) {
           if(null != value){
               try {
                   liveObservableData.setValue(JsonUtil.Str2JsonBean(value.string(), getTClass()));
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
        }

        @Override
        public void onError(Throwable e) {

        }

        @Override
        public void onComplete() {

        }
    });
}

/**
 * LiveData支持了lifecycle生命周期檢測
 * @return
 */
public LiveData<T> getLiveObservableData() {
    return liveObservableData;
}

/**
 * 當主動改變數據時重新設置被觀察的數據
 * @param product
 */
public void setUiObservableData(T product) {
    this.uiObservableData.set(product);
}

public Class<T> getTClass(){
    Class<T> tClass = (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    return tClass;
}

@Override
protected void onCleared() {
    super.onCleared();
    mDisposable.clear();
}

}

Android官方MVVM框架實現組件化之整體結構