Android 组件化案例

01.组件化实践开源项目

  • 关于组件化开发一点感想
    • 关于网上有许多关于组件化的博客,讲解了什么是组件化,为何要组件化,以及组件化的好处。大多数文章提供了组件化的思路,给我着手组件化开发提供了大量的便利。感谢前辈大神的分享!虽然有一些收获,但是很少有文章能够给出一个整体且有效的方案,或者一个具体的Demo。
    • 但是毕竟看博客也是为了实践做准备,当着手将之前的开源案例改版成组件化案例时,出现了大量的问题,也解决了一些问题。主要是学些了组件化开发流程。
    • 大多数公司慢慢着手组件化开发,在小公司,有的人由于之前没有做过组件化开发,尝试组件化也是挺好的;在大公司,有的人一去只是负责某个模块,可能刚开始组件化已经有人弄好了,那学习实践组件化那更快一些。业余实践,改版之前开源项目,写了这篇博客,耗费我不少时间,要是对你有些帮助,那我就很开心呢。由于我也是个小人物,所以写的不好,勿喷,欢迎提出建议!
  • 关于组件化开源项目
    • 项目整体架构模式采用:组件化+MVP+Rx+Retrofit+design+Dagger2+VLayout+X5
    • 包含的模块:wanAndroid【kotlin】+干货集中营+知乎日报+番茄Todo+精选新闻+豆瓣音乐电影小说+小说读书+简易记事本+搞笑视频+经典游戏+其他更多等等
    • 此项目属于业余时间练手的项目,接口数据来源均来自网络,如果存在侵权情况,请第一时间告知。本项目仅做学习交流使用,API数据内容所有权归原作公司所有,请勿用于其他用途。

02.如何创建模块

  • 根据架构设计图可以知道
    • image
  • 主工程:
    • 除了一些全局配置和主 Activity 之外,不包含任何业务代码。有的也叫做空壳app,主要是依赖业务组件进行运行。
  • 业务组件:
    • 最上层的业务,每个组件表示一条完整的业务线,彼此之间互相独立。原则上来说:各个业务组件之间不能有直接依赖!所有的业务组件均需要可以做到独立运行的效果。对于测试的时候,需要依赖多个业务组件的功能进行集成测试的时候。可以使用app壳进行多组件依赖管理运行。
    • 该案例中分为:干活集中营,玩Android,知乎日报,微信新闻,头条新闻,搞笑视频,百度音乐,我的记事本,豆瓣音乐读书电影,游戏组件等等。
  • 功能组件:
    • 该案例中分为,分享组件,评论反馈组件,支付组件,画廊组件等等。同时注意,可能会涉及多个业务组件对某个功能组件进行依赖!
  • 基础组件:
    • 支撑上层业务组件运行的基础业务服务。此部分组件为上层业务组件提供基本的功能支持。
    • 该案例中:在基础组件库中主要有,网络请求,图片加载,通信机制,工具类,分享功能,支付功能等等。当然,我把一些公共第三方库放到了这个基础组件中!

03.如何建立依赖

  • 关于工程中组件依赖结构图如下所示

    • image
  • 业务模块下完整配置代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    //控制组件模式和集成模式
    if (rootProject.ext.isGankApplication) {
    apply plugin: 'com.android.application'
    } else {
    apply plugin: 'com.android.library'
    }

    android {
    compileSdkVersion rootProject.ext.android["compileSdkVersion"]
    buildToolsVersion rootProject.ext.android["buildToolsVersion"]


    defaultConfig {
    minSdkVersion rootProject.ext.android["minSdkVersion"]
    targetSdkVersion rootProject.ext.android["targetSdkVersion"]
    versionCode rootProject.ext.android["versionCode"]
    versionName rootProject.ext.android["versionName"]

    if (rootProject.ext.isGankApplication){
    //组件模式下设置applicationId
    applicationId "com.ycbjie.gank"
    }
    javaCompileOptions {
    annotationProcessorOptions {
    arguments = [AROUTER_MODULE_NAME: project.getName()]
    }
    }
    }

    buildTypes {
    release {
    minifyEnabled false
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
    }

    //jdk1.8
    compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
    }

    sourceSets {
    main {
    if (rootProject.ext.isGankApplication) {
    manifest.srcFile 'src/main/module/AndroidManifest.xml'
    } else {
    manifest.srcFile 'src/main/AndroidManifest.xml'
    }
    jniLibs.srcDirs = ['libs']
    }
    }

    }

    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation project(':library')
    annotationProcessor rootProject.ext.dependencies["router-compiler"]
    }

04.如何统一配置文件

  • 由于组件化实践中模块比较多,因此配置gradle,添加依赖库时,需要考虑简化工作。那么究竟如何做呢?

  • 第一步,首先在项目根目录下创建一个yc.gradle文件。实际开发中只需要更改该文件中版本信息即可。

    • 我在网上看到的绝大多数案例,都是通过一个开关控件组件模式和集成模式的切换,但是这里我配置了多个组件的开关,分别控制对应的组件切换状态。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    ext {

    isApplication = false //false:作为Lib组件存在, true:作为application存在
    isAndroidApplication = false //玩Android模块开关,false:作为Lib组件存在, true:作为application存在
    isLoveApplication = false //爱意表达模块开关,false:作为Lib组件存在, true:作为application存在
    isVideoApplication = false //视频模块开关,false:作为Lib组件存在, true:作为application存在
    isNoteApplication = false //记事本模块开关,false:作为Lib组件存在, true:作为application存在
    isBookApplication = false //book模块开关,false:作为Lib组件存在, true:作为application存在
    isDouBanApplication = false //豆瓣模块开关,false:作为Lib组件存在, true:作为application存在
    isGankApplication = false //干货模块开关,false:作为Lib组件存在, true:作为application存在
    isMusicApplication = false //音乐模块开关,false:作为Lib组件存在, true:作为application存在
    isNewsApplication = false //新闻模块开关,false:作为Lib组件存在, true:作为application存在
    isToDoApplication = false //todo模块开关,false:作为Lib组件存在, true:作为application存在
    isZhiHuApplication = false //知乎模块开关,false:作为Lib组件存在, true:作为application存在
    isOtherApplication = false //其他模块开关,false:作为Lib组件存在, true:作为application存在

    android = [
    compileSdkVersion : 28,
    buildToolsVersion : "28.0.3",
    minSdkVersion : 17,
    targetSdkVersion : 28,
    versionCode : 22,
    versionName : "1.8.2" //必须是int或者float,否则影响线上升级
    ]

    version = [
    androidSupportSdkVersion: "28.0.0",
    retrofitSdkVersion : "2.4.0",
    glideSdkVersion : "4.8.0",
    canarySdkVersion : "1.5.4",
    constraintVersion : "1.0.2"
    ]

    dependencies = [
    //support
    "appcompat-v7" : "com.android.support:appcompat-v7:${version["androidSupportSdkVersion"]}",
    "multidex" : "com.android.support:multidex:1.0.1",
    //network
    "retrofit" : "com.squareup.retrofit2:retrofit:${version["retrofitSdkVersion"]}",
    "retrofit-converter-gson" : "com.squareup.retrofit2:converter-gson:${version["retrofitSdkVersion"]}",
    "retrofit-adapter-rxjava" : "com.squareup.retrofit2:adapter-rxjava2:${version["retrofitSdkVersion"]}",
    //这里省略一部分代码
    ]
    }
  • 第二步,然后在项目中的lib【注意这里是放到基础组件库的build.gradle】中添加代码,如下所示

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    apply plugin: 'com.android.library'

    android {
    compileSdkVersion rootProject.ext.android["compileSdkVersion"]
    buildToolsVersion rootProject.ext.android["buildToolsVersion"]


    defaultConfig {
    minSdkVersion rootProject.ext.android["minSdkVersion"]
    targetSdkVersion rootProject.ext.android["targetSdkVersion"]
    versionCode rootProject.ext.android["versionCode"]
    versionName rootProject.ext.android["versionName"]
    }
    }


    dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    api rootProject.ext.dependencies["appcompat-v7"]
    api rootProject.ext.dependencies["design"]
    api rootProject.ext.dependencies["palette"]
    api rootProject.ext.dependencies["glide"]
    api (rootProject.ext.dependencies["glide-transformations"]){
    exclude module: 'glide'
    }
    annotationProcessor rootProject.ext.dependencies["glide-compiler"]
    api files('libs/tbs_sdk_thirdapp_v3.2.0.jar')
    api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

    //省略部分代码
    }
  • 第三步,在其他model中添加依赖

    • implementation project(‘:library’)即可。

05.组件化的基础库

  • 基础库组件封装
    • 基础库组件封装库中主要包括开发常用的一些框架。可以直接看我的项目更加直观!
    • 1、网络请求(采用Retrofit+RxJava框架),拦截器
    • 2、图片加载(策略模式,Glide与Picasso之间可以切换)
    • 3、通信机制(RxBus),路由ARouter简单封装工具类(不同model之间通信)
    • 4、mvp框架,常用的base类,比如BaseActivity,BaseFragment等等
    • 5、通用的工具类,比如切割圆角,动画工具类等等
    • 6、自定义view(包括对话框,ToolBar布局,圆形图片等view的自定义)
    • 7、共有的shape,drawable,layout,color等资源文件
    • 8、全局初始化异步线程池封装库,各个组件均可以用到
  • 如何简化不熟悉组件化的人快速适应组件独立运行
    • 设置多个组件开关,需要切换那个组件就改那个。如果设置一个开关,要么把所有组件切成集成模式,要么把所有组件切成组件模式,有点容易出问题。更多可以往下看!
  • 严格限制公共基础组件的增长
    • 随着开发不断进行,要注意不要往基础公共组件加入太多内容。而是应该减小体积!倘若是基础组件过于庞大,那么运行组件也是比较缓慢的!

06.组件和集成模式如何切换

  • 在玩Android组件下的build.gradle文件,其他组件类似。

    • 通过一个开关来控制这个状态的切换,module如果是一个库,会使用com.android.library插件;如果是一个应用,则使用com.android.application插件
    1
    2
    3
    4
    5
    6
    //控制组件模式和集成模式
    if (rootProject.ext.isAndroidApplication) {
    apply plugin: 'com.android.application'
    } else {
    apply plugin: 'com.android.library'
    }
  • 集成模式如下所示

    • 首先需要在yc.gradle文件中设置 isApplication=false。Sync下后,发现该组件是library
    1
    2
    ext {
    isAndroidApplication = false //false:作为Lib组件存在, true:作为application存在
  • 组件模式如下所示

    • 首先需要在yc.gradle文件中设置 isApplication=true。Sync下后,发现该组件是application,即可针对模块进行运行
    1
    2
    ext {
    isAndroidApplication = true //false:作为Lib组件存在, true:作为application存在
  • 需要注意的地方,这个很重要

    • 首先看看网上绝大多数的作法,非常感谢这些大神的无私奉献!但是我觉得多个组件用一个开关控制也可以,但是sourceSets里面切换成组件app时,可以直接不用下面这么麻烦,可以复用java和res文件。
      • image
    • 接下来看看我的做法:
    • 下面这个配置十分重要。也就是说当该玩Android组件从library切换到application时,由于可以作为独立app运行,所以序意设置applicationId,并且配置清单文件,如下所示!
    • 在 library 和 application 之间切换,manifest文件也需要提供两套
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    android {
    defaultConfig {
    if (rootProject.ext.isAndroidApplication){
    //组件模式下设置applicationId
    applicationId "com.ycbjie.android"
    }
    }
    sourceSets {
    main {
    if (rootProject.ext.isAndroidApplication) {
    manifest.srcFile 'src/main/module/AndroidManifest.xml'
    } else {
    manifest.srcFile 'src/main/AndroidManifest.xml'
    }
    jniLibs.srcDirs = ['libs']
    }
    }
    }
    • 具体在项目中如下所示
    • image