(一)Android官方MVVM框架实现组件化之整体结构-程序员宅基地

技术标签: 【Android开发】  android  mvvm设计模式  框架  Android架构从入门到放弃  

作者: Dawish_大D
简书: http://www.jianshu.com/u/40e1f22c2c53

(一)Android官方MVVM框架实现组件化之整体结构
(二)Android官方MVVM框架实现组件化之ARouter串联各模块

目前的项目结构图置顶:Demo的Github地址: https://github.com/Dawish/GoogleArchitectureDemo

0-演示项目MVVM组件化架构图


一、google官方MVVM框架讲解

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

2-MVVM架构

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

3-ViewModel工厂

MVVM优势总结:

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

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

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

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

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


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

下图是项目模块和工程之间的依赖关系:
4-MVVM组件化示例项目架构图
下图是工程Android Studio中的目录结构:
5-工程目录结构

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

  • 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加进来编译。

3.2 ARouter串联各个模块

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

ARouter典型应用

  • 从外部URL映射到内部页面,以及参数传递与解析
  • 跨模块页面跳转,模块间解耦
  • 拦截跳转过程,处理登陆、埋点等逻辑
  • 跨模块API调用,通过控制反转来做组件解耦

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

我们在工程根目录下的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类,非组件化时排除此文件夹。
6-组件化非组件化编译切换

  • module下的AndroidManifest文件是组件化app编译时的,写了MAIN入口Activity
  • dubug下是组件化app编译时的Application类,初始化作为一个app运行时需要的资源等等。在非组件化编译在build.gradle文件中排除debug文件夹的所以东西。

3.4 最后预告:

后面会有一些列介绍在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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010072711/article/details/78633151

智能推荐

【C#】: Import “google/protobuf/timestamp.proto“ was not found or had errors.问题彻底被解决!_import "google/protobuf/timestamp.proto" was not f-程序员宅基地

文章浏览阅读3.7k次。使用C# 作为开发语言,将pb文件转换为cs文件的时候相信很多人都会遇到一个很棘手的问题,那就是protoc3环境下,import Timestamp的问题,在头部 import “google/protobuf/timestamp.proto”;的时候会抛异常:google/protobuf/timestamp.proto" was not found or had errors;解决办法【博主「pamxy」的原创文章的分享】:(注:之后才发现,不需要添加这个目录也可以,因为timestamp.p_import "google/protobuf/timestamp.proto" was not found or had errors.

安卓抓取JD wskey + 添加脚本自动转换JD cookie_jd_wsck-程序员宅基地

文章浏览阅读4.1w次,点赞9次,收藏98次。一、准备工具: 1. app:VNET(抓包用)、京东; 安卓手机需要下载VNET软件。下载官网:https://www.vnet-tech.com/zh/ 2. 已安装部署好的青龙面板;二、抓包wskey: 1. 打开已下载的VNET软件,第一步先安装CA证书; 点击右下角三角形按钮(开始抓包按钮),会提示安装证书,点击确定即可,app就会将CA证书下载至手机里,随后在手机设置里进行安装,这里不同手机可能安装位置不同,具体..._jd_wsck

Mybatis-Plus自动填充失效问题:当字段不为空时无法插入_mybatisplus插入不放为空的字段-程序员宅基地

文章浏览阅读2.9k次,点赞7次,收藏3次。本文针对mybatis-plus自动填充第一次更新能正常填充,第二次更新无法自动填充问题。????mybatis-plus自动填充:当要填充的字段不为空时,填充无效问题的解决????先上一副官方的图:取自官方:https://mp.baomidou.com/guide/auto-fill-metainfo.html第三条注意事项为自动填充失效原因:MetaObjectHandler提供的默认方法的策略均为:如果属性有值则不覆盖,如果填充值为null则不填充以官方案例为例:```java_mybatisplus插入不放为空的字段

Matlab 生成exe执行文件_matlab exe-程序员宅基地

文章浏览阅读1w次,点赞25次,收藏94次。利用 Application Complier 完成MATLAB转exe文件_matlab exe

Android下集成Paypal支付-程序员宅基地

文章浏览阅读137次。近期项目需要研究paypal支付,官网上的指导写的过于复杂,可能是老外的思维和中国人不一样吧。难得是发现下面这篇文章:http://www.androidhive.info/2015/02/Android-integrating-paypal-using-PHP-MySQL-part-1/在这篇文章的基础上,查看SDK简化了代码,给出下面这个例子,..._paypal支付集成到anroid应用中

利用JAVA代码将本地文件传入HDFS中_java上传文件到hdfs-程序员宅基地

文章浏览阅读1.9k次。先创建hdfs存储路径作为输出流,然后把本地文件存储路径作为输入流,利用copyBytes方法将本地文件上传到hdfs输出流的路径就大功告成了!在 C:\Windows\System32\drivers\etc 中修改hosts文件,将虚拟机的hostname和ip写在文件底部。2.在系统变量里配置HADOOP_HOME和JAVA_HOME,并配置PATH。首先要下载hadoop_lib包,然后在IDEA里面导入这个包。要一次性上传完这些文件,需要循环读取文件名字,代码如下。hdfs中存在文件,上传成功!_java上传文件到hdfs

随便推点

win11怎么装回win10系统_安装win10后卸载win11-程序员宅基地

文章浏览阅读3.4w次,点赞16次,收藏81次。微软出来了win11预览版系统,很多网友给自己的电脑下载安装尝鲜,不过因为是测试版可能会有比较多bug,又只有英文,有些网友使用起来并不顺畅,因此想要将win11退回win10系统。那么win11怎么装回win10系统呢?今天小编就教下大家win11退回win10系统的方法。方法一:1、首先点击开始菜单,在其中找到“设置”2、在设置面板中,我们可以找到“更新和安全”3、在更新和安全中,找到点击左边栏的“恢复”4、恢复的右侧我们就可以看到“回退到上版本的win10”了。方法二:_安装win10后卸载win11

SQL Server菜鸟入门_sql server菜鸟教程-程序员宅基地

文章浏览阅读3.3k次,点赞2次,收藏3次。数据定义_sql server菜鸟教程

Leetcode 数组(简单题)[1-1000题]_给定一个浮点数数组nums(逗号分隔)和一个浮点数目标值target(与数组空格分隔),请-程序员宅基地

文章浏览阅读1.9k次。1. 两数之和给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。示例:给定 nums = [2, 7, 11, 15], target = 9因为 nums[0] + nums[1] = 2 + 7 = 9所以返回 [0, 1]方法一..._给定一个浮点数数组nums(逗号分隔)和一个浮点数目标值target(与数组空格分隔),请

python性能优化方案_python 性能优化方法小结-程序员宅基地

文章浏览阅读152次。提高性能有如下方法1、Cython,用于合并python和c语言静态编译泛型2、IPython.parallel,用于在本地或者集群上并行执行代码3、numexpr,用于快速数值运算4、multiprocessing,python内建的并行处理模块5、Numba,用于为cpu动态编译python代码6、NumbaPro,用于为多核cpu和gpu动态编译python代码为了验证相同算法在上面不同实现..._np.array 测试gpu性能

GPT-ArcGIS数据处理、空间分析、可视化及多案例综合应用

GIS(地理信息系统)和GPT(生成式预训练模型)的结合正日益成为推动科研、城市规划、环境监测等领域发展的关键技术。GIS以其强大的空间数据处理、先进的空间分析工具、灵活的地图制作与可视化能力、广泛的扩展性和定制性,成为地理信息科学的核心工具。它在多学科领域的应用,如城市规划、环境科学、交通管理等,发挥着至关重要的作用。而GPT则以其在自然语言处理、文本生成、智能对话和知识库构建方面的优势,为GIS的智能化和自动化带来了新的可能性,提升了文本创作的效率和智能系统的交互体验。

在Debian 10上安装MySQL_debian mysql安装-程序员宅基地

文章浏览阅读324次。MySQL是一个著名的开源数据库管理系统,用于存储和检索各种流行应用程序的数据。MySQL 是*LAMP*堆栈中的**M**,LAMP 堆栈是一组常用的开源软件,还包括 Linux、Apache Web 服务器和 PHP 编程语言。在本教程中,您将通过添加此存储库来安装最新版本的 MySQL,安装 MySQL 软件本身,确保安装安全,并测试 MySQL 是否正在运行并响应命令。_debian mysql安装

推荐文章

热门文章

相关标签