126

MVVM框架实现组件化之整体结构

 6 years ago
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 分钟。

Image

google官方MVVM框架讲解

我前面对比了MVC和MVP《两张图看懂Android开发中MVC与MVP的区别》,可以相对于MVC我们的MVP是有多优越,但是Android开发现在已经开始流行了MVVM,前不久google官方发布了MVVM的正式库。官方的正式MVVM库主要包括下面四个:

其中只有ViewModel是MVVM结构中的一个组件,其他的三个都是辅助性质的。

lifecycles 就是处理UI界面的生命周期,在26版本以后的Support库中,AppCompatActivitySupportActivity中都实现了LifecycleOwner,内部已经对UI界面的生命周期做了处理了。

LiveData是一个抽象类,我们可以存放UI页面需要的数据,就是把数据包装在LiveData中了,我们可以观测LiveData中的数据变化,但是LiveData是跟UI的生命周期关联的,当UI页面销毁了,LiveData的数据变化回调是不会执行的。

Room 就是一个sqlite数据持久化库,我们也可以使用别的ORM库。

MVVM架构优势

《两张图看懂Android开发中MVC与MVP的区别》 前面两张图真是了MVC和MVP的区别,我这里也来一张图看看MVVM:

看上图ModelView是不会发生关系的,ViewModel是把View和Model关联起来的加工厂:

MVVM优势总结:

  1. ViewModel双向绑定,一方的改变都会影响另一方,开发者不用再去手动修改UI的数据。额,互相自动的。

  2. 不需要findViewById也不需要butterknife,不需要拿到具体的View去设置数据绑定监听器等等,这些都可以用DataBinding完成。是不是很舒服?

  3. ViewModel的双向绑定是支持生命周期检测的,不会担心页面销毁了还有回调发生,这个由lifeCycle完成。

  4. 不会像MVC一样导致Activity中代码量巨大,也不会像MVP一样出现大量的ViewPresenter接口。项目结构更加低耦合。

  5. 更低的耦合把各个模块分开开发,分开测试,可以分给不同的开发人员来完成。

MVVM组件化示例项目架构分析

下图是项目模块和工程之间的依赖关系:

下图是工程Android Studio中的目录结构:

各模块和彼此之间的关系解释:

  • lib_opensource :第三方build.gradle依赖,本项目主要有supportlifecycleroomfrescoretrofitokhttpRxJavaARouter这些。

  • lib_coremodel: 存放MVVM中的ModelViewModel两个模块,就是数据的处理和数据与UI页面的绑定。依赖lib_opensource库。

  • lib_common : 公共库,主要有各种base,各种ui组件,自定义组件,公用的Activity、公用的Fragment,和公用的utils等等。依赖lib_coremodel库。

  • module_girls : 妹子功能模块,可以在libraryapplication之间切换,自己可以是一个app也可以成为别的app的一个组件模块。组件化编译时为app,反之为module。

  • module_news : 新闻功能模块,可以在libraryapplication之间切换,自己可以是一个app也可以成为别的app的一个组件模块。组件化编译时为app,反之为module。

  • app_universal : 定制版本的app,组件化编译时 module_girlsmodule_news为app,所以不能把这两个作为module加进来编译,所以组件化编译时app_universal要依赖lib_common库,反之就可以把 module_girlsmodule_news作为module加进来编译。

  • app_specific : 定制版本的app,组件化编译时 module_girlsmodule_news为app,所以不能把这两个作为module加进来编译,所以组件化编译时app_specific要依赖lib_common库,反之就可以把 module_girlsmodule_news作为module加进来编译。

ARouter串联各个模块

使用ARouter来跳转Activity和获取Fragment,记得看之前别人的组件化结构文章,一直都在纠结Fragment的获取问题,我想说的是有了ARouter来获取Fragment不是超级简单么?

ARouter典型应用

  • 从外部URL映射到内部页面,以及参数传递与解析

  • 跨模块页面跳转,模块间解耦

  • 拦截跳转过程,处理登陆、埋点等逻辑

  • 跨模块API调用,通过控制反转来做组件解耦

组件化编译和非组件化编译切换

我们在工程根目录下的gradle.properties文件中加入一个Boolean类型的变量,通过修改这个变量来识别编译模式:



# 每次更改“isModule”的值后,需要点击 "Sync Project" 按钮
# isModule是“集成开发模式”和“组件开发模式”的切换开关
isModule=false

然后在 module_girlsmodule_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和获取FragmentDataBinding实现数据和UI的互相绑定、Rxjava2Retrofit2动态数据获取,和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 赞赏通道

Image



代码人生,一飞冲天。

Image


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK