【Android】组件化打造第三方知乎日报系列(一)——项目架构搭建

本节完整代码可以前往github查看,项目地址:https://github.com/N0tExpectErr0r/Zhihu-Daily

写在前面

我为什么下定决心要写这样一个系列呢?

第一个原因是写博客的过程中受到了朋友的提示,慢慢意识到了一个问题——自己之前所写的博客都是很零散的知识点,更多是面向自己的学习笔记。这样写博客是无法练习自己的表达能力的,同时这样的博客也不会有人愿意看的。

第二个原因是马上接手的项目中使用到了组件化架构,这对于我这样一个大二的菜鸡来说是算是一个全新的领域,因此准备以系列博客的形式来督促自己学习组件化。

从第二点可以看出,我本人也是初步接触组件化,也就是说这一个系列并不是为了手把手教读者如何将组件化应用在项目中的(我也深知自己没有这个能力),而是希望读者可以和我一起,一步步探索组件化架构的方方面面。在此希望各位能通过我的文章学习到一些东西。

关于更新频率

每周不低于三次,直至完结。

关于我

笔者目前是大二学生,技术还不够成熟,文章中难免会出现一些错误,希望路过的大佬们可以及时指出。

组件化架构介绍

组件化的思想其实就是把常用的功能、控件等等全部抽离封装,将业务拆成一个个独立的模块(Module)来进行管理。所有的独立模块依赖于基础库,组件之间则没有依赖。这样可以做到每个模块能够单独运行,而在正式发布时则通过App壳工程(AppModule)将这些模块拼凑形成一个完整的App。通过路由(ARouter进行页面的跳转,通过事件总线(EventBus)来进行各个模块间的通信。

它的优点如下:

  • 解耦
  • 结构清晰,便于维护
  • 可提高编译的速度

初步架构介绍

先说一下目前暂定的项目整体的基本架构,这里参照了《Android组件化架构》一书中的基础组件化架构。可以看到,项目由三层构成,从上到下分别是:应用层、组件层、基础层。

  • 应用层: 空壳Module,统筹所有组件,输出生成App
  • 组件层: 一些简单的业务,如本次项目中的妹子图模块、文章模块等等
  • 基础层: 一些基础库及对基础库的封装,如图片加载、网络加载、数据储存等等功能。被所有组件所引用。

这样的一个架构十分简单清晰,目前暂时采用此架构,以后如果有必要可以进一步进行改进。

项目初步配置

在开始我们的项目之前,我们要对项目进行一些基本的配置,为日后的开发打下基础。

version.gradle

这里采取了一个常用的处理,创建了一个version.gradle,把所有的版本信息都在这里管理,这样以后想要某个改变某个库的版本的时候就只需要改变我们的version.gradle。

ext{
    COMPILE_SDK_VERSION = 28
    MIN_SDK_VERSION = 19
    TARGET_SDK_VERSION = 28

    VERSION_CODE = 1
    VERSION_NAME = "1.0"

    SUPPORT_LIB_VERSION = '28.0.0'

    AROUTER_VERSION = "1.2.4"
    AROUTER_COMPILER_VERSION = "1.1.4"

    OKHTTP_VERSION = "3.10.0"
    RETROFIT_VERSION = "2.8.5"

    EVENTBUS_VERSION = "3.1.1"

    BUTTERKNIFE_VERSION = "8.4.0"

    GREENDAO_VERSION = "3.2.2"

    RXJAVA_VERSION = "2.2.1"
    RXANDROID_VERSION = "2.1.0"
    RXLIFECYCLE_VERSION = "2.0.1"

    GLIDE_VERSION = "4.8.0"
}

之后,在Project的build.gradle中加入下面这句代码,来引用我们的version.gradle

apply from: "version.gradle"

关于单独编译module的设置

我们知道,组件化开发的时候我们一般都是只需要负责开发、调试自己的模块,而发布的时候则需要把所有组件整合到一起来。因此我们在这里加入一个开关来决定是单独编译还是组合编译。

在gradle.properties中加入如下的一个变量,它决定了是否是单独编译。

isSingleBuild = false

这样,我们就可以改变这个boolean类型的值从而改变编译的方式。

对于组件层的模块,我们希望在单独编译时每个模块以独立的应用的方式运行,而在组合编译的时候则是以Library的形式能被其他模块引用,可以使用如下的方式:

if(isSingleBuild.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

而在app的module中,则这样配置:

if(!isSingleBuild.toBoolean()){
    // 组合的模块
    implementation projext(':xxModule')
    ...
}

同时,我们需要的是在单独编译的时候使用模块自己的Manifest,而在组合编译的时候使用app模块的Manifest。因此我们通过判断isSingleBuild来采用不同的Manifest。

sourceSets{
    main{
        if(isSingleBuild.toBoolean()){
            manifest.srcFile '../<模块名>/src/main/AndroidManifest.xml'
        }else{
            manifest.srcFile '../app/src/main/AndroidManifest.xml'
        }
    }
}

为了防止资源文件命名冲突,我们在gradle中配置资源文件前缀来防止资源文件冲突,然后我们在创建资源的时候一定要在资源名前加上模块名前缀。

android{
    ...
    resourcePrefix "<模块名>_"
}

创建BaseModule

刚刚说到,Base模块是一个Library模块,来被其他Module引用,

apply plugin: 'com.android.library'
...

Manifest则直接指定AppModule的

manifest.srcFile '../app/src/main/AndroidManifest.xml'

之后我们导入在BaseModule中需要用到的各类库。需要注意的是,这里不要指明库的版本,而是引用我们在version.gradle中定义的版本变量。

dependencies{ 
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation "com.android.support:appcompat-v7:${SUPPORT_LIB_VERSION}"
    implementation "com.alibaba:arouter-api:${AROUTER_VERSION}"
    annotationProcessor "com.alibaba:arouter-compiler:${AROUTER_COMPILER_VERSION}"
    ...
 }

最后别忘了在其他组件中引用BaseModule。

 implementation project(':base')

BaseModule基于各种库的封装

关于怎么封装给各个上层组件调用的易用工具库的封装,这里就不细讲了,原因有三点:

一、 这个系列的重点在于组件化,我们应当更多关注组件化的细节

二、关于如何封装每个人都有自己的想法,因此这里应该大家各自发挥

三、个人认为自己在封装的方面十分弱,就不再献丑了

大家如果是跟着我的进度的话,可以按自己的思路封装一套库出来使用。

下面是我的BaseModule的目录结构

  • app
    • DailyApplication.java
  • bus
    • EventBuser.java
  • component
    • activity
    • BaseActivity.java
    • DailyBaseActivity.java
    • DailyMvpBaseActivity.java
    • fragment
    • BaseLazyFragment.java
    • BaseFragment.java
    • DailyMvpFragment.java
    • DailyBaseFragment.java
    • LazyFragment.java
    • lifecycle
    • AutoDisposer.java
    • LifecycleFullObserver.java
    • LifecycleSimpleObserver.java
    • LifecycleAttachObserver.java
    • mvp
    • BaseContract.java
  • gson
    • Gsoner.java
  • image
    • engines
    • GlideEngine.java
    • ImageEngine.java
    • ImageLoader.java
    • LoadOptions.java
  • router
    • RouteActivity.java
    • RouterConstant.java
  • rx
    • RxSchedulers.java
  • util
    • HandlerUtil.java
    • Gsoner.java
    • JsonUtil.java
    • Timer.java
    • ToastUtil.java
N0tExpectErr0r

N0tExpectErr0r

一名热爱代码的 Android 开发者

留下你的评论

*评论支持代码高亮<pre class="prettyprint linenums">代码</pre>