组件化开发+换肤

最近提出这么一个需求:需要在原来的项目之上快速改造并打包一个新的项目出来。
这就需要:
1、快速换肤;
2、组件化开发,可以快速组合需要的功能;

组件化 / 模块化

说到组件化 / 模块化,那么什么是组件化 / 模块化呢?组件化和模块化的区别又在哪里呢?

组件,就是我们对功能的封装,一个功能就是一个组件,数据库、网络、文件操作、社会化分享等等这些功能都是组件。我们之所以要搞出组件的概念,是为了能够让我们的上层业务模块能够随时依赖和调用这些基础功能。组件基本上可以分为基础功能组件、通用 UI 组件、基础业务组件等这几类。所以为了满足上述要求,组件必须具有较高的独立性、扩展性以及复用性。
模块,就是对一系列有内聚性的业务进行整理,将其与其它业务进行切割、拆分,从主工程或原所在位置抽离为一个相对独立的部分。仅仅针对业务而言,比如说我们可以把订单业务独立为为一个模块,可以把个人中心独立为一个模块,把用户登录独立为一个模块等,在 App 中的体现就是一个个独立的 Git 仓库。模块化的一个好处是用到时可以搭积木,比如可以多个工程间复用同一个或几个业务模块,比如腾讯的 QQ 和 TIM,除了 UI 界面外 TIM 显然复用了大量现有的原 QQ 工程的业务模块代码,当然,我们这里暂时并没有这个需求。

开始

入门:
Android组件化开发实践
Android组件化方案
Android组件化之终极方案

这3篇文章都算是一个系列的了,适合刚入门的时候看,仿照demo调试。

进阶:
滴滴国际化项目 Android 端演进
Android彻底组件化方案实践(得到Android组件化方案)
安居客Android项目架构演进
蜂鸟商家版 iOS 组件化 / 模块化实践总结

成熟项目的架构值得研究

终极:
京东iOS客户端组件管理实践

像工厂一样,将所有业务做成一个个组件,在可视化界面上完成配置,一键生成APP,类似于“应用内包含应用”,可以说是最理想的方案了。

模块间组件共用

在拆分业务模块的过程中,经常发生两个业务模块同时引用某一块业务代码的问题,这时我们就需要对这一块代码进行理解,首先区分它到底应不应该划分到业务层来?

如果是的话,应该划归到哪一个模块中去更合理一些;
如果不是的话,应该将这一部分代码下沉到哪一个组件库中去比较合适,或者独立为一个组件。

如果两个模块需要用到共同的数据模型,那么可以下沉到CommonBusiness中。

单独测试

从入门级文章来看,每个模块的调试其实都是在模块自己内部测试的,这会造成2个问题:
1、产生多余代码
2、涉及到多模块调试的时候必须得回到主app壳才能调试。

《聚美组件化实践之路》中给出了一个解决方案,那就是每个模块内不进行测试,而是每个模块再单独创建一个app壳调试,这样如果涉及到跨模块调试时,就可以导入另一个模块了。缺点是在前期的话可能会多一点代码量,不过这对后期很有帮助。

这里写图片描述

这种分层结构的好处有:

1、业务组件不再在library与application之间进行切换。开发环境统一,不易出现环境切换冲突。

2、app壳单独独立出来。可以在壳工程中添加一些特有的独立代码,由于各自的壳功能不会参与到主app壳中去进行编译,所有这里面你可以针对各自的业务。添加一些独立的入口管理类。比如添加一个RootActivity,在此添加一个可以跳转到任意页面的列表,方便进行测试运行等。

如果是每个模块都需要的功能,可以抽离出来作为特色功能组件,让每个模块都去依赖,比如说登录组件。如下图中的核心业务模块:

这里写图片描述

Butter Knife问题

在各个Library中switch必须改为if..else..形式
R.id.xx改为R2.id.xx

在Android library中不能使用switch-case语句访问资源ID的原因分析及解决方案
Resource IDs cannot be used in a switch statement in Android library modules

资源名重复问题

为了避免不同组件之间资源名重复,在每个组件的build.gradle中增加resourcePrefix “xxx_”,从而固定每个组件的资源前缀。

android {
resourcePrefix "sample"
}

这个资源前缀的作用是:当你在该module下创建了一个资源命名时,若名字不能与此前缀进行匹配,则将会进行即时提醒。避免冲突。而不是说会自动将我们的文件都加上这个前缀。

组件单独调试

并指定不同的AndroidManiFest文件和java文件。
这里写图片描述
在project的gradle.properties中:

# 每次更改“isRunAlone”的值后,需要点击 "Sync Project" 按钮
isRunAlone=false

在module中:

if (isRunAlone.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
android {
    //指定jniLibs的库路径
    sourceSets {
        main {
            if (isRunAlone.toBoolean()) {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                //集成开发模式下排除debug文件夹中的所有Java文件
                java {
                    exclude 'debug/**'
                }
            }
            jniLibs.srcDirs = ['libs']
        }
    }
    }

路由和intent的对比

显式intent:直接的类依赖,耦合严重
隐式intent:配置规则都是在Manifest,扩展性较差;跳转过程无法控制,导致了在跳转失败的情况下无法降级,而是会直接抛出运营级的异常;规则集中式管理,协作困难。
H5中是无法使用StartActivity()跳转到Native页面的,而从Native跳转到H5页面也只能通过配置浏览器的方式实现。

使用ARouter

productFlavors

除了组件化,有一些问题可以用productFlavors来解决,比如说:

当两个版本的服务器地址不一样时:

productA{ 
buildConfigField ‘String’, ‘HOST’, ‘”http://192.168.0.208:7001/“’ 
}

productB{ 
buildConfigField ‘String’, ‘HOST’, ‘”http://192.168.0.30:7001/“’ 
}

假如我一个现场的主页面要导航页的5个功能,另外一个现场只要其中的4个功能,导致功能性的差异,这时候使用productFlavors怎么做?
这里我提供一种解决思路,也是使用String变量来存,但是存放的值是Json字符串

            buildConfigField 'String', 'workBenchMenu', '"[{\\"title\\": \\"所有公文\\",\\"icon\\": \\"workbench_review\\"},{\\"title\\": \\"消息\\",\\"icon\\": \\"workben_message\\"},{\\"title\\": \\"任务\\",\\"icon\\": \\"workben_task\\" },{\\"title\\": \\"联系人\\",\\"icon\\": \\"workben_contact\\"},{\\"title\\": \\"单位动态\\",\\"icon\\": \\"workbench_dynamic\\"},{\\"title\\": \\"行程\\",\\"icon\\": \\"workben_schedule\\"},{\\"title\\": \\"公文分类\\",\\"icon\\": \\"workbench_documentrank\\"}]"'

然后Activity里面通过解析该json去生成布局,实现功能性差异。

——来自利用Gradle的productFlavors使用应用多现场配置

有时我们发布到市场的应用希望有不同的版本,比如:免费版和收费版。这就需要我们来构建不同的应用变体。那么我们可以在productFlavors中进行相应的配置,来生成不同的应用。如:

android {
    defaultConfig {
        applicationId "com.example.myapplicationtest"
    }
    productFlavors {
        free {
            applicationIdSuffix ".free"
        }
        paid {
            applicationIdSuffix ".paid"
        }
    }
}

在上面的代码中,我们用”free”表示免费版,用”paid”表示付费版。在productFlavors中,通过配置不同应用ID,最终生成不同的应用。最终这两种应用apk可同时存在于市场中。

Android applicationId与包名的区别

如何解决编译慢的问题?

独立发布

无法独立发布会带来什么问题?非常明显,慢!插件化一段时间后,我们发现慢的问题严重影响着我们的效率。在这个阶段,我们已经有超过十个团队,iOS工程的源码文件超过一万个。由于主工程是通过各插件的源码组合起来的,每一次重新索引和编译,都要消耗超过半个小时的时间。

要解决这个问题,就是要把插件化进行到底,实现插件的另外两个独立——独立开发和独立运行。最重要的工作就是我们今天的主题解耦,梳理各个插件之间的依赖关系。让每一个独立插件尽可能少的依赖其他插件,在最小范围内正常编译执行。每次发布不再是一个稳定版本号,而是一个稳定的二进制包。

如此依赖,我们把超过半小时的编译过程拆分到数十个模块中,而主工程依赖数十个二进制包,编译也就快了。

解决方法:发布二进制包

——手机天猫解耦之路

终极方案

京东iOS客户端的解决方案:

去应用化
如果公司的所有应用全部都组件化,并且组件间统一协议通信,那么应用最终的输出方式应该就像工厂加工一样,加工过程就是组件组合过程,出厂时贴上标签。听起来很理想,事实上是可以做到的,从iBiu系统上线到目前一个月时间我们接入管理着三个应用包括主App,超过100个组件。


组件配置表
组件如何组合被抽象出一个组件配置表,记录了不同应用的组件配置,对应到具体的组件责任人,版本,对接产品,测试以及开发,通过工具一键完成组件的发布与集成等工作。

这里写图片描述

让应用可以包含应用(这里所说的应用是一个虚拟的应用,或者叫做Collection更合适)。Collection可以任意包含另外的Collection,同时可以拿到Collection下的组件,如上图,App-A这个Collection最终是有另外三个Collection下的组件所组合而成。

组件开发完成后,向后台提交,就会自动创建git库。

开发者选择组件组合安装。

如果在某个版本需要更新icon或启动图怎么办?有没有可以统一这些配置的办法,而又没必要让开发者人为的替换管理?答案是肯定的,我们在iBiu可视化工具中加入了应用版本的配置拉取脚本功能。简单点讲就是安装组合时执行远程脚本。在iBiu后台针对应用版本配置相应的脚本,可以修改工程任意配置。
这里写图片描述

——京东iOS客户端组件管理实践

参考文章

Android组件化方案
Android组件化之终极方案
Android 彻底组件化方案实践
关于Android模块化你需要知道的
终极组件化框架项目方案详解
开源最佳实践:Android平台页面路由框架ARouter
安居客Android项目架构演进
京东iOS客户端组件管理实践
手机天猫解耦之路
聚美组件化实践之路
谁阻碍了你做组件化开发?
组件化实践详解(一)
组件化实践详解(二)
Android 开发:由模块化到组件化(一)
组件化架构漫谈
Android 彻底组件化 demo 发布
Android 组件化 —— 路由设计最佳实践
关于终端业务组件化的几点思考

利用Gradle的productFlavors使用应用多现场配置

猜你喜欢

转载自blog.csdn.net/iromkoear/article/details/79858091