(一)Android官方MVVM框架實現元件化之整體結構
一、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架構優勢
看上圖Model
和View
是不會發生關係的,ViewModel
是把View和Model關聯起來的加工廠:
MVVM優勢總結:
View
和Model
雙向繫結,一方的改變都會影響另一方,開發者不用再去手動修改UI的資料。額,互相自動的。不需要
findViewById
也不需要butterknife
,不需要拿到具體的View
去設定資料繫結監聽器等等,這些都可以用DataBinding
完成。是不是很舒服?View
和Model
的雙向繫結是支援生命週期檢測的,不會擔心頁面銷燬了還有回調發生,這個由lifeCycle
完成。不會像
MVC
一樣導致Activity
中程式碼量巨大,也不會像MVP
一樣出現大量的View
和Presenter
介面。專案結構更加低耦合。更低的耦合把各個模組分開開發,分開測試,可以分給不同的開發人員來完成。
三、MVVM元件化示例專案架構分析
下圖是專案模組和工程之間的依賴關係:
下圖是工程Android Studio中的目錄結構:
3.1 各模組和彼此之間的關係解釋:
lib_opensource
:第三方build.gradle依賴,本專案主要有support
、lifecycle
、room
、fresco
、retrofit
、okhttp
、RxJava
、ARouter
這些。lib_coremodel
: 存放MVVM中的Model
和ViewModel
兩個模組,就是資料的處理和資料與UI頁面的繫結。依賴lib_opensource
庫。lib_common
: 公共庫,主要有各種base
,各種ui元件,自定義元件,公用的Activity
、公用的Fragment
,和公用的utils
等等。依賴lib_coremodel
庫。module_girls
: 妹子功能模組,可以在library
和application
之間切換,自己可以是一個app
也可以成為別的app的
一個元件模組。元件化編譯時為app,反之為module。module_news
: 新聞功能模組,可以在library
和application
之間切換,自己可以是一個app
也可以成為別的app
的一個元件模組。元件化編譯時為app,反之為module。app_universal
: 定製版本的app,元件化編譯時module_girls
和module_news
為app,所以不能把這兩個作為module加進來編譯,所以元件化編譯時app_universal
要依賴lib_common
庫,反之就可以把module_girls
和module_news
作為module加進來編譯。app_specific
: 定製版本的app,元件化編譯時module_girls
和module_news
為app,所以不能把這兩個作為module加進來編譯,所以元件化編譯時app_specific
要依賴lib_common
庫,反之就可以把module_girls
和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
然後在 module_girls
和module_news
中的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();
}
}