AppJoint2-2023再看安卓组件化框架

news2024/11/15 21:09:45

零、什么是组件化

为了避免一些小伙伴一脸懵的进来,又一脸懵的出去,我先简单的说一下什么是组件化。

开发程序时,我们都希望功能间的耦合度尽可能的低,这样的好处是:便于并行开发、代码易于维护、出问题时也好定位。

所以降低功能间的耦合度就是我们开发项目时的一个需求。

具体到安卓开发当中,我们可以对一个具体的功能模块建立一个Module,然后该功能的需求全都在该Module内实现。最后在最上层建立一个Application Module,打包的时候就让他依赖需要的功能就好了。

这样的实现同样会引入问题:Module间的调用问题,因为在结构上来说Module都是同级的且谁也不依赖谁,同样的谁也看不到谁的方法。

这就是组件化要解决的核心问题,就是Module间功能调用的问题。

当然标准答案应该是:

  1. Module间四大组件的调用。
  2. Module间的方法功能调用。

但是在AppJoint的设计思想中,将以上两个需求合并成为了一个需求。

明确了需求之后我们在需要做的就是开发一个可以实现该功能的框架。这样的框架一般来说都叫路由框架,这里十分推荐大家看一下 ARouter 的实现原理,基本上现有路由框架的实现思路和使用的技术技巧都可以在里面看到影子。

接着我们说一下路由框架的一般实现方式:

  1. 当前Module对其他Module开放一个接口,该接口有当前Modules所有可对外开放的方法。
  2. 当前Module内部实现该接口,并实现对外开放方法;同时使用带有路径参数的注解标记Activity、Fragment 方便外部的调用。
  3. 路由框架完成接口与接口实现的映射关系;同时完成路径与Activity、Fragment之间的映射关系。
    这一步是各个路由框架主要需要提供的功能。
  4. 其他组件,通过路由框架,传入接口,调用对外开放的方法;传入路径启动Activity或者获得Fragment。

这里再说一下为什么调用四大组件也是可以归并为方法调用。

首先组件间是不知道彼此有什么Activity的,但是Module内部是知道;所以我们可以在接口的实现方法中开放启动Activity的能力:

    override fun startActivityOfApp(context: Context) {
        val intent = Intent(context, AppActivity::class.java)
        context.startActivity(intent)
    }

那么在不知道Activity的类时,也只需要传入上下文就可以启动该Activity了。
获取Fragment和上面的方式类似,给方法加一个返回值就可以了。

所以至此,我们仅需要对外开放接口,就可以解决组件化引入的问题。

一、AppJoint2的由来

最开始接触组件化应该是在2020年的时候,那个时候进行多种的技术选型,主要考虑的维度就是尽可能引入一个轻量的框架,对现有程序做最小的修改。

基于这个考虑我们使用了 AppJoint ,这个框架非常的轻量,不看插件部分的话的核心类100行左右,注解也只有三个。

随着时间的推移,原作者不再维护该库,而Gradle不断的升级,这导致出现了下面的问题:

在这里插入图片描述
这个问题我也复现分析了一下:
在这里插入图片描述
红框内的内容为编译时插件在做 Transform 时读取编译好的Jar包名称。这里之前Gradle版本读取的是正确的Jar Name ; 而新版的Gradle读取的并非是字符串意义上的Jar Name,这就导致依赖Jar Name的逻辑出错,进一步导致框架运行报错。

报错代码为:

if (appJointClassFile == null) {
      throw new RuntimeException("AppJoint class file not found, please check \"io.github.prototypez:app-joint-core:{latest_version}\" is in your dependency graph.")
}

因为找不到目标Jar包,所以也找不到要注入的类,此时框架就会报错,编译终止。

所以为了解决以上的问题,同时由于Transform 从 Gradle 8+ 开始删除了该API,也就是说插件部分需要做较大修改,所以才有了该库。

二、优化内容

  1. Gradle版本升级适配
    原版使用:AGP 3.4.2 + Gradle 5.1.1
    适配至: AGP 8.0.1 + Gradle 8.1.1
    截止到发稿日期 Gradle 最新版本为 8.1.1。

    不再使用Transform(Gradle 8+开始删除了该API),更改为Task实现。
    目前插件模块不存在Deprecated的方法。

  2. 将原版:
    Java(Core)+Kotlin(Demo)+Groovy(Plugin) 切换为
    Kotlin(Core)+Kotlin(Demo)+Kotlin(Plugin)
    即全部切换为Kotlin实现
    在这里插入图片描述
    由于切换Kotlin带来的问题已经全部适配解决。

  3. 优化掉和框架无关的信息,整体突出AppJoint2。

  4. 优化项目结构:

框架组件名称功能
appjoint2-annotation声明项目中所有涉及的注解
appjoint2-core路由的内核
appjoint2-gradle-plugin生成路由代码的插件
Demo组件名称功能
app组件化最上层的壳
module-A展示组件化能力的 module-A
module-B展示组件化能力的 module-B
module_common组件的共有依赖

5.优化现有的数据结构,当前的业务需求可以使用更简单的数据结构。优化完毕后内核只有一个AppJoint2一个类。

6.增加日志模块配置参数,可以更灵活的查看编译日志;为不同等级的日志配置颜色,方便查看插件编译日志。

在这里插入图片描述
蓝色为Debug、绿色为Info、黄色为Warning。

三、引入AppJoint2

  1. 添加 jitpack.io
    项目级 settings.gradle 添加:
pluginManagement {
    repositories {
        maven { url "https://jitpack.io" }
    }
}
dependencyResolutionManagement {
    repositories {
        maven { url "https://jitpack.io" }
    }
}
  1. 添加插件地址:
    项目级 build.gradle 添加:
buildscript {
    dependencies {
        classpath "com.github.JustGank:AppJoint2-Plugin:1.0.3"
    }
}
  1. 引入插件
    Application Module 添加 'com.appjoint2.plugin'
plugins {
    id 'com.android.application'
    //添加插件
    id 'com.appjoint2.plugin'
}
  1. 添加依赖
    可以放到底层Module,方便上层参与打包的Module都可以访问到。
dependencies {
	api 'com.github.JustGank:AppJoint2-Class:1.0.0'
}

四、如何使用

4.1 标记主Application

@AppSpec
class AppApplication : Application()

4.2 标记对外开放服务的实现

@ServiceProvider
class ModuleAServiceImpl : ModuleAService

由于接口可以有多实现,所以这里也支持自定义实现的别名如:

@ServiceProvider("another")
class AppServiceImpl2 : AppService

注:如果使用了别名,那么调用Service的时候也要传入对应的别名。

4.3 标记子Module的Application

@ModuleSpec(priority = 2)
class ModuleAApplication : Application()

此处支持优先级,优先级值越低,越先被加载,他们的生命周期和主Application同步。

4.4 调用对外开放服务的方法

AppJoint2.service(ModuleBService::class.java)?.startActivityOfModuleB(context)

4.5 控制编译日志

引入插件后,可以在主Application Module 的 build.gradle 中最外层声明:

AppJoint2LogConfig  {
    openLog true
    debug  false
    outputTime false
}
  • openLog:是否输出编译日志。
  • debug:是否输出debug日志,输出的日志更为详细,方便调试模式场景下查看日志。
  • outputTime:是否输出格式化时间。

五、增量编译

由于使用新的Task方式实现,经测试目前直接支持增量编译,之前一直存在的不支持增量编译的问题已经不再存在。

六、源码以及Demo

AppJoint的源码以及路径:
https://github.com/PrototypeZ/AppJoint

AppJoint2:
https://github.com/JustGank/AppJoint2

七、参考-致谢

整个AppJoint2的开发并不是一件太简单的事,这个过程中做了很多的的学习、思考与实践。
这里面给出涉及的参考并致谢:

  1. 扩展 Android Gradle 插件
    https://developer.android.google.cn/studio/build/extend-agp?hl=zh_cn

  2. 你的插件想适配Transform Action? 可能还早了点
    https://juejin.cn/post/7190196880469393463

  3. Transform API 废弃了,路由插件怎么办?
    https://juejin.cn/post/7222091234100330554

  4. 为什么说TransformAction不是Transform的替代品
    https://juejin.cn/post/7218847310309064741

  5. Gradle User Manual
    https://docs.gradle.org/current/userguide/userguide.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/564324.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

抖音seo矩阵系统源码开发开源型私有化部署方案

抖音SEO矩阵系统是基于抖音平台的搜索引擎优化技术的一种系统,其主要作用是通过一系列的技术手段,提高抖音视频的曝光和排名,使其获得更多的流量和粉丝。在本文中,我们将介绍抖音SEO矩阵系统的开发技术,包括系统设计、…

map reduce实现累加器

需求:数组长度为100,每一项为对应下标,累加求和。 切题思路: 1.如何声明一个长度为100的数组?答:new Array(100) 2.数组每一项如何比前一项1 答:map(item,index)index为数组下标,…

企业推行OKR的必要条件

今天我们的话题是“OKR在企业落地执行,有哪些必要条件?” 对于有落地 OKR 经验的人可能更深有感触,OKR理解起来容易,但落地起来却困难重重,常言道“万事开头难”,那接下来我们就先从落地 OKR 的先决条件开始说起吧。 …

我的创作纪念日,成为创作者的第512天

机缘 从事编程岗一有将近4年的时光了,但正在开始总结写博客还是一年前,是在百度搜素资料了解到的CSDN开发者社区。在CSDN认识了很多技术大牛,他们的文章记录了他们的学习路径,看到他们从小白一步一步成长为大牛,这才下…

大数运算(加法,减法,乘法,除法)

目录 一.大数加法 1.题目描述 2.问题分析 3.代码实现 二.大数减法 1.题目描述 2.问题分析 3.代码实现 三.大数乘法 1.题目描述 2.问题分析 3.代码实现 四.大数除法 1.题目描述 2.问题分析 3.代码实现 一.大数加法 1.题目描述 以字符串的形式读入两个数字&#…

Smartbi电子表格故事之高效营销活动后的自助数据分析

自助数据分析是BI的潮流,但几乎都是数据可视化流派,Smartbi电子表格另辟蹊径,在Excel中提供自助分析的能力,然后通过服务器进行发布,这个功能我们称之为“Excel融合分析”,目前在免费版中即可体验。 系统数…

Python脚本如何定时运行

一、背景 很多时候,我们使用Python编写好的各种脚本,会有定时运行的需求,希望脚本能每天在指定的时间点定时运行,比如:定时发送消息、定时发送邮件、定时执行自动化测试脚本等。 定时运行脚本常用的有2种方式&#x…

LeetCode:30. 串联所有单词的子串

30. 串联所有单词的子串 1)题目2)思路3)代码4)结果 1)题目 给定一个字符串 s 和一个字符串数组 words。 words 中所有字符串 长度相同。 s 中的 串联子串 是指一个包含 words 中所有字符串以任意顺序排列连接起来的子…

CentOS 7安装redis

一、概述 1、redis介绍 Redis 全称 Remote Dictionary Server(即远程字典服务),它是一个基于内存实现的键值型非关系(NoSQL)数据库 2、redis的特点 支持数据持久化 redis支持数据的持久化,可以将内存中的…

IDEA 2022.2 安装以及自定义优化

IDEA2022.2 安装以及自定义优化 文章目录 IDEA2022.2 安装以及自定义优化安装图解获取激活码自定义优化文件编码设置设置类文档注释和方式注释模板方法分割线 常用插件离线安装 安装图解 静默卸载(旧版本的设置和配置将不会被删除) 获取激活码 略…

芯片设计_IC行业到底还值不值得入?

前几天哲库的关停让不少想入行IC的人人心惶惶,这也让不少人引起思考,IC行业的未来发展如何?还值不值得入行?给大家分析一下原因,大家就可自行判断到底可不可入?还能火多久? 一、分析芯片行业市场情况 芯片是典型的周期成长行业&#xf…

flowable的流程任务统计sql(续)

继续之前的文章,我们这里还需要进行按月统计,同时为了适应jeecg的图形显示,所以做了一些调整如下: 1、按月统计任务数量 select DATE_FORMAT(c.days, %Y%m) months, count(num) num from (SELECT DATE_FORMAT(START_TIME_, %Y-%m…

u盘无法复制过大文件怎么解决?揭秘!

大家在使用U盘的时候,有没有发现有的文件不能够复制到U盘,这是怎么回事呢?其实是文件太大了,那么u盘无法复制过大文件怎么解决?本文给大家推荐了3个靠谱好用的方法,一起来学习一下! 一、案例分享 “求救!求救!买了一…

栈和队列的概念和实现

栈 栈 定义:只能在一端进行插入或删除操作的的线性表 主要特点:后进先出 存储结构的实现 顺序存储结构 链式存储结构 用途:通常作为一种临时存放数据的容器。如果后存入的元素先处理则使用栈。比如用于保存函…

工业设备为什么要实现状态监测?

在现代工业生产中,实现状态监测对于工业设备的运行和维护至关重要。状态监测是一种通过实时数据采集和分析来监测设备运行状况的技术,它能够提供对设备健康状况的准确评估,并帮助预测潜在故障和优化维护计划。 在设备管理领域,我们…

2023 年互联网就业怎样?

来说说我们公司最近的情况: 15K的Java后端程序员,岗位发布一天,收到简历212份; 28K的高级全栈工程师,岗位发布6小时,收到简历349份; 技术主管不信邪,200/天的Python实习生&#xff…

月薪10k和月薪25k的软件测试人员有什么区别?看完你就不会再迷茫了

了解软件测试这行的人都清楚,功能测试的天花板可能也就15k左右,而自动化的起点就在15k左右,当然两个岗位需要掌握的技能肯定是不一样的。 如果刚入门学习完软件测试,那么基本薪资会在7-8k左右,这个薪资不太高主要是因…

《消息队列高手课》课程笔记(四)

如何确保消息不会丢失? 检测消息丢失的方法 我们可以利用消息队列的有序性来验证是否有消息丢失。 在 Producer 端,我们给每个发出的消息附加一个连续递增的序号,然后在 Consumer 端来检查这个序号的连续性。如果没有消息丢失,Consumer 收…

Goat: Fine-tuned LLaMA Outperforms GPT-4 on Arithmetic Tasks

Goat: Fine-tuned LLaMA Outperforms GPT-4 on Arithmetic Tasks IntroductionMethod语言模型数学问题学习能力COT 实验参考 Introduction 大语言模型现在已经展示了很好的效果在各种下游任务,如GPT4。但是GPT的数学能力还没有达到最优。 在这篇文章中&#xff0c…

三角化:Triangulation: Why Optimize?

三角化:Triangulation: Why Optimize? 1. 预备知识1.1 评估 3D 点准确性 2. 提出的方法2.1 广义加权中点法2.2 可选的中点法2.3 Cheirality(多视图几何中代表着3D点的正景深约束)2.4 逆深度加权中点-Inverse Depth Weighted(IDW) Midpoint 3. 实现代码 Reference: …