医动力Android基于CC组件化框架的探索与实践

为什么要组件化?

医动力App作为公司的核心产品已经有多年历史了,随着版本的不断迭代,功能越来越多,代码量越来越大,不可避免的会产生一下问题:

  • 业务越来越复杂,维护成本高;
  • 业务耦合度高,代码越来越臃肿,团队内部多人协作开发困难;
  • 编译时间长,每修改一处代码后都要重新编译打包测试,导致非常耗时;
  • 开发测试困难,每次修改都必须打包整个项目运行.

因此,为了提高项目的可维护性和开发效率,组件化成为了必然.

组件化的目标

首先看看老版本的医动力的项目工程结构

项目分为Doctor和Patient,它们之间公共的代码放在common中,common中会依赖一些第三方的库.随着项目的迭代,Doctor和Patient里面的代码会越来越耦合,因为单个module中仅仅是以包名作为功能的划分,而包之间是可以随意调用的.

AndroidStudio/IDEA是多模块管理的,组件化的思路就是把不同的业务模块拆分到各个子Module中,同时这些业务Module之间不会存在直接的调用,这样当我们移除项目中的某个业务Module的时候不会影响整体的项目运行,如下图所示:

可以看到有「ModuleIM」,「ModuleFollow」,「ModuleDiary」三个业务模块,它们都会引用Common模块而获取一些基础库的支持,同时这些业务模块是可以单独运行的.后续会将更多的模块独立出来,完成彻底的组件化.

如何组件化?

Android的组件化的技术点主要在两个方面:

  • 配置组件独立运行的能力;
  • 组件之间的通信

第一点是通过在module的gradle.build中切换

apply plugin: 'com.android.application'
apply plugin: 'com.android.library',
复制代码

并设置对应模式下加载的AndroidManifest.xml和src的路径.

第二点由于组件之间是没有直接依赖的关系的,要想让它们通信就必须把它们注册在一个公共的地方,这里可以通过路由,也可以通过注册接口.

目前组件化的方案在网上有很多,由于组件化并不涉及Android系统级别的操作,因此是比较成熟稳定的.我们知道,组件化是需要花费大量时间和精力的,很难做到把项目彻底的拆分成组件,那有没有一种方式可以让我们不改动原有项目(改动很小)的情况下,将新功能进行组件化开发?

因此我们选择了使用CC来进行组件化的改造

Component Caller(CC)介绍

业界首个支持渐进式组件化改造的Android组件化开源框架

引用自CC官方的两张图能让我们很快的明白什么叫做渐进式组件化?

在渐进式组件化的方案中,可以先不用解耦,只需要让单独运行的组件能够调用到主App中的功能即可。思路是这样的:

  • 新业务以组件形式开发
  • 新组件需要调用的主App中的业务,在对应的模块中创建一个组件类,对外暴露对应的服务,供其它组件调用,并不需要现在就将这个模块解耦
  • 新组件通过跨App的方式调用主App中的组件
  • 主App也可以通过跨App的方式调用到单独运行的组件App中的组件
  • 在同一个module中可以创建多个组件类,将来解耦时将对应的组件类移动到解耦后的module中即可

关于CC的技术实现细节可以查看其github主页的wiki系列文章 github.com/luckybilly/…

组件化实践

CC的集成

1.在项目的根gradle.build中加入:

buildscript {dependencies {... ...classpath 'com.billy.android:autoregister:1.4.1'}
}复制代码

这个autoregister插件是用来在编译期间动态扫描并修改class文件,实现组件的自动注册,具体配置后面会提到

2.在Doctor和Patient这两个主App的gradle.build中加入

ext.mainApp = true  //设置为true,表示此module为主app module,一直以application方式编译
apply from: '../cc-setting.gradle'
复制代码

cc-setting.gradle中主要进行了以下几点操作:

  • 读取local.properties文件中的配置来区分组件是否以独立app的方式编译;
  • 添加com.billy.android:cc:1.1.0依赖;
  • 自动注册组件的配置

3.实现IComponent接口创建组件类

我们统一在Doctor和Patient中的exports包内创建App对外提供的组件,不同业务的组件放在创建在对于的类中管理

IComponent的实现也很简单,提供一个模块名称name和对不同action的处理

主App的配置就这么多,接下来要新建一个组件Module

4.业务module的配置

这里需要设置module独立运行时的applicationId,同时指定resourcePrefix资源前缀(防止不同模块之间资源文件的冲突)

因为设置了module的独立运行,就需要准备一份module在独立运行模式下的AndroidManifest文件,路径在src/main/debug下

5.业务module提供IComponent接口

和主App一样,模块要提供服务给其他模块调用,需要提供实现IComponent的子类,因为业务module提供的服务会比较少且单一,我们将它放在包名下的ExportComponent下

7.主App设置需要依赖的module

dependencies {api fileTree(include: ['*.jar'], dir: 'libs')addComponent 'module_follow'addComponent 'module_diary'addComponent 'module_im'
}
复制代码

通过addComponent方式添加依赖,其内部会根据业务module的运行模式决定是否依赖

6.设置业务module的运行模式

业务module的运行模式包括开发模式和集成模式

  • 开发模式:会以App的方式运行
  • 集成模式:打包在主App中

打开local.properties文件

module_follow=true
module_diary=true
module_im=false
复制代码

设置模块名称等于true或者false(没有设置则为false)

  • 当等于true则该模块会以App运行,这时候打包主App的时候是不会把该模块打包进去
  • 当等于false时则不能独立运行,打包主App的时候会一起打包进去

运行调试

现在你可以将组件单独运行起来了,但是由于业务组件是不包含登录功能的,因此它是没有用户登录状态的,所以我们需要通过CC的组件调用去主App中获取

补充一点,如果想要跨App调用首先需要打开CC的设置

CC.enableRemoteCC(true);
复制代码

组件调用

CCResult result = CC.obtainBuilder("common").setActionName("httpInfo").addParam("type", type).build().call();String baseUrl = result.getDataItem("baseUrl");String token = result.getDataItem("token");
复制代码

调用的方式很简单,只需要指定模块名称和对应的Action,同时可以传递参数,以上代码调用的是common模块的action为httpInfo的组件.

同时支持同步和异步的调用,前一个例子是同步调用,异步调用需要传入一个回调方法.

String callId = CC.obtainBuilder("Common").setActionName("httpInfo").addParam("type",type).build().callAsync(new IComponentCallback(){...});
复制代码

被调用组件在处理完请求后,需要作出响应,看以下代码:

public class HttpComponent implements IComponent {@Overridepublic String getName() {return "http";}@Overridepublic boolean onCall(CC cc) {String action = cc.getActionName();if(action.equals("action1")){CC.sendCCResult(cc.getCallId(),CCResult.success());return false;}else if(action.equals("action2")){String ccId = cc.getCallId();return true;}}
}复制代码

注意onCall()方法的返回值,action1中返回的是false,而action2返回的是true,作用是:

  • false表示立即回调结果,这里需要调用 CC.sendCCResult(cc.getCallId(),CCResult.success() 告诉调用发返回成功
  • true则表示延迟回调结果,这时你可以拿着ccCallId,等到事情处理完后才调用CC.sendCCResult(cc.getCallId(),CCResult.success()

这有什么作用呢? 比如你在调用一个登录组件去到登录界面,只有登录成功了才返回结果,这就需要延迟回调.

网络请求组件化

项目中用到的网络请求框架是okhttp+retrofit,我们希望不同module中使用不同的retrofit的Service实例,比如在Module_follow中我们会创建FollowService.java来处理当前模块的网络请求

在集成开发模式下我们可以通过组件调用去获取主App中的Retrofit对象,我们只需要在主App中定一个name=http,action=getRetrofit的IComponent;

但在开发模式下,跨进程调用组件是传输不了Retrofit对象的,因为Android的跨进程只能传输Parcelable对象,这里我们可以在本module中提供一个相同名称的IComponent,在里面去获取主App的baseUrl和token,并创建新的Retrofit对象,这样就可以透明的处理获取Retrofit对象了.

为什么以上方式可行呢?因为CC在进行组件化调用的时候,会检查当前模块是否存在要调用的模块,如果存在则会调用本地的,不存在才会去跨进程调用.最后我们可以把这些模拟操作抽到一个lib_mock的module里面复用.

组件化小结

组件化后带来的一些变化:

  • 编译时间明显缩短
  • 开发人员之间可以通过模块分工
  • 可以在模块中尝试新的技术而不担心影响全局(Kotlin)

CC已知局限:

开发模式不能在Android8.0及以上环境运行,开发的时候可以使用虚拟机或者低版本的手机,集成模式不影响.

引用

CC:基于总线的android组件化开发框架
Android彻底组件化方案实践

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

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

相关文章

使用协同过滤推荐电影

ALSO, ARE RECOMMENDER SYSTEMS INFLUENCING OUR TASTE??此外,推荐系统是否影响我们的口味? An excerpt on creating a movie recommender system similar to the OTT platforms.有关创建类似于OTT平台的电影推荐系统的摘录。 INTRODUCTION介绍 For…

423. 从英文中重建数字

423. 从英文中重建数字 给你一个字符串 s ,其中包含字母顺序打乱的用英文单词表示的若干数字(0-9)。按 升序 返回原始的数字。 例 1:输入:s "owoztneoer" 输出:"012"示例 2&#xf…

锦欣生殖获战略投资,华平、信银领投,红杉、药明康德跟投

9月16日消息,锦欣生殖近日宣布已完成新一轮的战略投资,本轮融资由原战略股东华平投资及新引入的中信银行旗下信银投资领投,红杉资本中国基金、药明康德等跟投。完成本轮融资后,华平投资及信银投资分别成为锦欣生殖的第二及第三大股…

数据暑假实习面试_面试数据科学实习如何准备

数据暑假实习面试Unfortunately, on this occasion, your application was not successful, and we have appointed an applicant who…不幸的是,这一次,您的申请没有成功,我们已经任命了一位符合以下条件的申请人: Sounds famili…

两道简单的入门题

1&#xff09;  for循环求100以内奇数和 1 #include<stdio.h> 2 int main(){ 3 int ans0;//定义一个答案变量存储答案 4 for(int i1;i<100;i)//用for从1循环到100&#xff0c;如果i%2&#xff01;0&#xff08;%是一种取余运算&#xff09; 5 if(…

1716. 计算力扣银行的钱

1716. 计算力扣银行的钱 Hercy 想要为购买第一辆车存钱。他 每天 都往力扣银行里存钱。 最开始&#xff0c;他在周一的时候存入 1 块钱。从周二到周日&#xff0c;他每天都比前一天多存入 1 块钱。在接下来每一个周一&#xff0c;他都会比 前一个周一 多存入 1 块钱。 给你 …

谷歌 colab_如何在Google Colab上使用熊猫分析

谷歌 colabRecently, pandas have come up with an amazing open-source library called pandas-profiling. Generally, EDA starts by df.describe(), df.info() and etc which to be done separately. Pandas_profiling extends the general data frame report using a singl…

【题解】HAOI2007分割矩阵

水题盛宴啦啦啦……做起来真的极其舒服&#xff0c;比某些毒瘤题好太多了…… 数据范围极小 --> 状压 / 搜索 / 高维度dp&#xff1b;观察要求的均方差&#xff0c;开始考虑是不是能够换一下式子。我们用\(a_{x}\)来表示第 \(x\) 个矩阵的总值&#xff0c;则式子为&#xff…

Java之生成Pdf并对Pdf内容操作

虽说网上有很多可以在线导出Pdf或者word或者转成png等格式的工具&#xff0c;但是我觉得还是得了解知道是怎么实现的。一来&#xff0c;在线免费转换工具&#xff0c;是有容量限制的&#xff0c;达到一定的容量时&#xff0c;是不能成功导出的;二来&#xff0c;业务需求&#x…

边际概率条件概率_数据科学家解释的边际联合和条件概率

边际概率条件概率Probability plays a very important role in Data Science, as Data Scientist regularly attempt to draw statistical inferences that could be used to predict data or analyse data better.P robability起着数据科学非常重要的作用&#xff0c;为数据科…

1822. 数组元素积的符号

1822. 数组元素积的符号 已知函数 signFunc(x) 将会根据 x 的正负返回特定值&#xff1a; 如果 x 是正数&#xff0c;返回 1 。 如果 x 是负数&#xff0c;返回 -1 。 如果 x 是等于 0 &#xff0c;返回 0 。 给你一个整数数组 nums 。令 product 为数组 nums 中所有元素值的…

java并发编程实战:第十四章----构建自定义的同步工具

一、状态依赖性管理 对于单线程程序&#xff0c;某个条件为假&#xff0c;那么这个条件将永远无法成真在并发程序中&#xff0c;基于状态的条件可能会由于其他线程的操作而改变1 可阻塞的状态依赖操作的结构2 3 acquire lock on object state4 while (precondition does not ho…

关于之前的函数式编程

之前写的函数式编程是我从 JavaScript ES6 函数式编程入门经典这本书里面整理的&#xff0c;然后只在第一篇里专门提到了&#xff0c;后面的话没有专门提到&#xff0c;而且引用了书中大量的文字&#xff0c;所以我把掘金这里的文章都删除了&#xff0c;然后在 CSDN 上面每一篇…

袋装决策树_袋装树是每个数据科学家需要的机器学习算法

袋装决策树袋装树木介绍 (Introduction to Bagged Trees) Without diving into the specifics just yet, it’s important that you have some foundation understanding of decision trees.尚未深入研究细节&#xff0c;对决策树有一定基础了解就很重要。 From the evaluatio…

[JS 分析] 天_眼_查 字体文件

0. 参考 js分析 猫_眼_电_影 字体文件 font-face 1. 分析 1.1 定位目标元素 1.2 查看网页源代码 1.3 requests 请求提取得到大量错误信息 对比猫_眼_电_影抓取到unicode编码&#xff0c;天_眼_查混合使用正常字体和自定义字体&#xff0c;难点在于如何从 红 转化为 美。 一开始…

深入学习Redis(4):哨兵

前言在 深入学习Redis&#xff08;3&#xff09;&#xff1a;主从复制 中曾提到&#xff0c;Redis主从复制的作用有数据热备、负载均衡、故障恢复等&#xff1b;但主从复制存在的一个问题是故障恢复无法自动化。本文将要介绍的哨兵&#xff0c;它基于Redis主从复制&#xff0c;…

1805. 字符串中不同整数的数目

1805. 字符串中不同整数的数目 给你一个字符串 word &#xff0c;该字符串由数字和小写英文字母组成。 请你用空格替换每个不是数字的字符。例如&#xff0c;“a123bc34d8ef34” 将会变成 " 123 34 8 34" 。注意&#xff0c;剩下的这些整数为&#xff08;相邻彼此至…

经天测绘测量工具包_公共土地测量系统

经天测绘测量工具包部分-乡镇第一师 (Sections — First Divisions of Townships) The PLSS Townships are typically divided into 36 Sections (nominally one mile on a side), but in the national standard this feature is called the first division because Townships …

洛谷 P4012 深海机器人问题【费用流】

题目链接&#xff1a;https://www.luogu.org/problemnew/show/P4012 洛谷 P4012 深海机器人问题 输入输出样例 输入样例#1&#xff1a; 1 1 2 2 1 2 3 4 5 6 7 2 8 10 9 3 2 0 0 2 2 2 输出样例#1&#xff1a; 42 说明 题解&#xff1a;建图方法如下&#xff1a; 对于矩阵中的每…

day5 模拟用户登录

_user "yangtuo" _passwd "123456"# passd_authentication False #flag 标志位for i in range(3): #for 语句后面可以跟else&#xff0c;但是不能跟elifusername input("Username:")password input("Password:")if username _use…