MVVM框架实现组件化之整体结构
source link: http://mp.weixin.qq.com/s/ogeEaXfz3hHz4yl3zQzlwQ
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
阅读文本大概需要 6.6 分钟。
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优势总结:
View
和Model
双向绑定,一方的改变都会影响另一方,开发者不用再去手动修改UI的数据。额,互相自动的。不需要
findViewById
也不需要butterknife
,不需要拿到具体的View
去设置数据绑定监听器等等,这些都可以用DataBinding
完成。是不是很舒服?View
和Model
的双向绑定是支持生命周期检测的,不会担心页面销毁了还有回调发生,这个由lifeCycle
完成。不会像
MVC
一样导致Activity
中代码量巨大,也不会像MVP
一样出现大量的View
和Presenter
接口。项目结构更加低耦合。更低的耦合把各个模块分开开发,分开测试,可以分给不同的开发人员来完成。
MVVM组件化示例项目架构分析
下图是项目模块和工程之间的依赖关系:
下图是工程Android Studio中的目录结构:
各模块和彼此之间的关系解释:
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加进来编译。
ARouter串联各个模块
使用ARouter
来跳转Activity
和获取Fragment
,记得看之前别人的组件化结构文章,一直都在纠结Fragment
的获取问题,我想说的是有了ARouter
来获取Fragment
不是超级简单么?
ARouter典型应用
从外部URL映射到内部页面,以及参数传递与解析
跨模块页面跳转,模块间解耦
拦截跳转过程,处理登陆、埋点等逻辑
跨模块API调用,通过控制反转来做组件解耦
组件化编译和非组件化编译切换
我们在工程根目录下的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
文件夹的所以东西。
最后预告
后面会有一些列介绍在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();
}
}
Demo的Github地址: https://github.com/Dawish/GoogleArchitectureDemo
作者:doodlewind
博客地址: http://www.jianshu.com/u/40e1f22c2c53
iOS 赞赏通道
代码人生,一飞冲天。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK