Android官方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框架實現組件化之整體結構