Android插件化与组件化

技术背景

gradle

是什么,为什么,怎么做

gradle 是用来编译构建的工具, 支持多种语法。 编译构建这一行为, 也是一项工程, 有多种构建工具, 使用不同的语言, 提供不同的方式, 支持不同的工具。编译构建是生成可执行文件的前期工作。

组件式开发

组件式开发主要是为了解耦, 各模块可以独立编译,可以相互组合,相互通信。

AUC 项目分析

AUC项目源码

原作者对此框架相关的文章:
AucFrame 之简介
AucFrame 之让你的Gradle 更智能
AucFrame 之统一管理Gradle
比EventBus 更高效的事件总线
一学就会的模块间通信

为何还是要再分析一下呢?
主要原因是我看完上面几篇文章还是不能明白, 也无法顺利应用到AS4.1(Gradle6.1.1)版本.
我看不明白原因, 一是对gradle插件开发技能不熟悉, 二是根据作者画的框架图,以我现有的能力不能实现出来。
因此,我希望提供一个新手用户的角度分析一下这个框架的视角, 提升自己的同时希望能帮助后来人。

要解决什么问题

达到组件化开发, 必须模块解耦, 因此得解决模块间通信, 模块间页面跳转问题。
所谓组件式开发, 是将各模块可实现最小功能(不可再拆分),可以灵活快速组合成一个功能不同的应用。此时各最小功能的模块便是组件, 可以单独作为一个应用测试。

用什么技术实现

grrovy语言开发
gradle插件开发

如何使用AUC

AUC主要还是一种思想, 可以完全按照其模板结构来搭建框架, 那样gradle脚本就不用大肆修改。
当然也可以自己定制,那么就需要更系统的学习gralde脚本开发技术。

  1. config.json 中的appConfig, pkgConfig, proConfig 都是用来干什么的

setting.gradle 中主要还是根据config.json 中的配置来include 工程, 同时更新Config.java中 /*Never 之间的的内容, 用于builLib.gradle 中依赖的配置

config.json 中的 pkgConfig, 对应的配置是模块的业务层pkg
config.json 中的 appConfig, 对应的配置是模块的启动调试入口app, 整个app真正的入口只有一个

appConfig 与 pkgConfig中的优先级大于 proConfig,
打开与关闭哪些app与具体的业务是在 appConfig 与 pkgConfig 这两个数组中配置, 为空,默认所有的pkg 与 app模块都不编译。 多人合作的话,务必将这两个数组保持为空, 各人配置自己的模块开发。
其他库模块是在proConfig中配置的, proConfig 是全配置,export,pkg, app, lib 都需在时面配置好, appConfig 与 pkgConfig 也必须是proConfig中有的。

  1. 各模块间的配置

根本编译的那套流程还是复用Android 本身的DSL脚本, 定制的逻辑在 buildApp.gradle 和 buildLib.gradle 中, 主要是针对dependencies 进行定制

各模块是否还需要依赖?
依赖就偶合了,那也没有必要进行组件式开发了,
但是共公lib库还是需要依赖的, 带有具体的业务逻辑的模块可以解耦

每个模块的export, pkg, app 都是什么角色?

通用lib库是通过依赖顺序是 common -> base -> subutil -> utilcode
其他业务库
    export模块 都依赖 通用lib库 common
    pkg 和 mock 模块都依赖 config.json 中配置的 apply=true 的 export模块

这里重复依赖的问题由gralde 解决,会使编译的速度变慢,
全面依赖肯定会有多余的类,会使编译速度变慢,但是多余的类和多余的方法会在混淆时由proguard优化掉
export库是各模块间的通信耦合暴露引用对象, 可以通过BusUtil来依赖注入

app模块依赖
    跟据Config.pkgConfig 来依赖所有的pkg模块

最终编译, 所有的模块还是需要依赖且放在一起的
app模块 –(依赖)–> pkg模块 –(依赖)–> export模块 –(依赖)–> common模块
但是每个模块又可以分开测试, 框架复用了 lib 和 app库的gradle, 将配置工作集中在了 config.json 中
因此模块间的通信也是有必要的,就像EventBus 避免了接口回调地狱, 也将各模块间的耦合性降低了。
每个模块必须有一个export, 否则就不能依赖 common lib库

遵循单一原则,开放封闭原则,依赖倒置原则, 里氏替换原则,接口隔离原则,最少感知原则

可解耦模块间的通信是如何做到的?

BusUtils 解决了数据的传递, 传递的bean对象是否还是需要抽取出公共模块依赖呢?

ApiUtils 解决了不同模块间页面的跳转

最后编译apk的时候不还是要将所有的模块都编译进来?
这个环节在setting中就配置好了, 是将所有的模块都编译进来,只要对应的模块可以编译通过,那就是好的OK的

  1. debug模式下, applicationIdSuffix 与 resValue问题

APT: error: attribute ‘package’ in tag is not a valid Android package name: ‘com.sophimp.android.cook-studio.debug’

`applicationIdSuffix ".debug"`,
    在原有的applicationId 加一个后缀, 可以针对不同的flavor, 不同的 buildType, 让多代理商,调试版本与发布版本共存
`resValue "string", "app_name", Config.appName + suffix`
    用于传参, 第一个表示类型,第二个是参数名称,第三个是参数值

上面的错误与这两个属性设置没关系,是包名中不能有中划线
  1. 静态工具类确实很方便, 使用那么多静态方法好吗?

    静态方法与静态变量在程序初始化的时候就会加载到内存, 如果有很多方法后续没有被使用,会加大内存负担与程序运行负担(现在大内存的情况下,这个影响并不是很严重,且添加混淆后,也会去除没有引用的静态方法)
    静态变量在多线程或多处修改时,安全性不能保证。
    静态方法也只适合工具类,适合实现可重入的方法,
    静态方法是面向过程的,意味着面向对象的特性就没法使用了。
    静态方法不会被回收,所以容易引起内存泄漏。
    所以, 必要的工具类,对整个项目性能的影响可忽略。

  2. AUC的使用感受

    移植到项目中后,创建模块更加灵活,因为build.gralde可以复用,所以只需要创建特定名字的文件夹, 在config.json中配置即可。
    使用到了插件开发的buildSrc模块,可以将build.gradle的书写达到提示效果。
    相比于ARouter, WMRouter, 结构更加简洁, 便于学习,对于个人项目使用, 足够了。
    原项目暂时还不支持androidx, 暂时没用上AndroidStudio自带的迁移工具(可能是我不会用吧), 纯手工移植过来的。

    androidx 迁移库工件映射

    编译的时候慢了很多, 内存消耗了更多。 当然,相比于同时打开几个Android Studio 还是要消耗还是要小一些的。
    mock层为每个组件提供了测试模拟环境
    pkg用来装载组件的业务功能
    export库是各模块间的通信耦合共享引用的对象
    app用来作为测试入口

    对于多个项目,还是分开放在多个工程里, 多个项目中功能相同的模块,采用独立git维护,然后每个项目再写一个脚本工具,类似于aosp的repo来管理是最好的。
    没必要将不同的项目放在同一个工程里,那样维护起来也麻烦。这里面主要的问题就是共用模块的问题。

gradle插件开发

  1. groovy 学习

gradle 开发相关的资料

Gradle Get Started
Gradle User Manual
Gradle 用户指南官方文档中文版
Developing Custom Gradle Plugins

生命周期
Gradle构建生命周期和Hook技术

利用gradle 构建的生命周期,预留出来的接口。

buildSrc 模块是gradle 第一个执行的入口

计算机里通用的思想: 甭管什么框架, 什么系统, 都是先提供一个上下文环境, 而开发工作都是在这个环境中来工作,所以,既然是一个运行时环境,
那么就有环境里预质的变量和功能,扩展接口,二次开发也都是基于此来开发。

如何基于这个思想来学习框架呢?

找到入口函数, 生命周期,提供的环境变量,扩展的接口。 至于学习语言的语法,提供的api 有了业务思路以后, 都是可以现学现卖的。
至于语言以及其提供的基础库,也无外乎那几个套路,最本质的东西还是算法和数据结构。

计算机学习的套路
如何按照这个套路来学习新的框架技术
完全的新手如何去学,
先用起来, 人的学习是多元化感知的,仅仅靠文字表述,还是缺少了很多信息,在 用过程,会增视觉听觉感觉等综合性的信息,下意识就会全面掌握一种知识。

  1. DSL语法原理

DSL语法原理与常用API介绍

DSL的写法实际就是Project方法中的一个闭包参数, 这与很多脚本语言内嵌json或table一样,直接将文件加载到内存, 即时解释执行。

  1. 定制的思想

build.gradle复用,
dependency的智能填充
多渠道,多版本实现
自动打包命名,签名功能

模块间通信

其他组件化框架

美团开源WMRouter
美团外卖Android开源路由框架

总结

大前端(web, 移动,pc) 的思想确实都是相通的,移动端相对于 pc, web, 发展晚一些,组件化,插件化在移动端是新技术,但是对于其他平台,却不一定。
因此学习技术的时候,眼光不能局限于某一平台,这也是我在学习过程中,总是疑问的一点: 为什么那么多轮子,开源框架,别人都能想到,我就不能想到,为什么别人学得那么快,而我的效率这么低呢?

组件化,主要是为了逻辑更清晰,功能解耦, 后续的扩展,在编码上形成的约束规则。

插件化, 主要是线上更新的应用场景下产生的需求,要求动态,无感知更新,更快发版,灵活快速的应对线上bug修复, 但是插件化不是万能的,有些场景还是需要发版来解决。而且插件化也触及了应用平台的利益。
开发与平台方也是各种博弈,导致插件化产生各种所谓的黑科技,但实效性又短。

版权声明:除特殊说明,博客文章均为Sophimp原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。 由于可能会成为AI模型(如chatGPT)的训练样本,本博客禁止将AI自动生成内容作为文章上传(特别声明时除外)。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇