组件化开发有什么好处?

1、当项目越来越大时,app的业务越来越复杂,会出现业务功能复杂混乱,各功能块、页面相互依赖,相互调用太多导致耦合度高,而采用组件化开发,我们就可以将功能模块合理的划分,降低功能耦合度。
2、不采用组件化开发时,编译速度缓慢,修改一个页面布局编译一下还得等几分钟。使用组件开发后,每次修改只需要编译对应的模块即可。
3、有利于团队协作开发,开发人员之间职责明确,每一个开发人员只需要关注和负责自己的功能点,互不干扰,提高效率。
4、在多渠道或者合作方需求不一致时可以快速拼接功能模块打包相应的App。

组件化开发项目结构

组件化就是要将项目的各个功能拆成多个模块,可分为app主模块,登录注册模块,个人中心模块,功能模块等。

组件化架构设计:

1、主工程模块,主要是APP壳,里面不涉及任何逻辑代码,只有权限等配置写在app模块下的AndroidManifest.xml中。

2、常规业务模块,该层的组件就是我们真正的业务组件了。我们通常按照功能模块来划分业务组件,例如注册登录、用户个人中心、APP的首页模块等。这里的每个业务组件都是一个小的APP,只需要修改一下对应的module的build.gradle,就可以单独编译,单独打包成APK在手机上运行。

3、功能组件,一个公共模块,所有的常规业务模块都依赖他。字符串、颜色、尺寸资源等写在该模块下,该组件是一些通用的工具类。

组件之间必须遵循以下规则:
1、只有上层的组件才能依赖下层组件,不能反向依赖,否则可能会出现循环依赖的情况;
2、同一层之间的组件不能相互依赖,这也是为了组件之间的彻底解耦;

常规业务模块间的Activity跳转

由于常规业务模块之间是不能相互依赖的,所以不能直接使用startActivity进行跳转。在这里介绍两种方法:

方法1:
用功能组件(common组件)来统一管理业务组件间的跳转。

业务组件间不能相互依赖,而A组件需要跳转到B组件,那么我们可以让B组件来提供具体的跳转方法,让common组件来承担这个跳转方法的调用,然后A组件依赖common组件即可,是一个迂回的策略。

其他的组件要跳转到另外的组件,也是同样的处理。随着业务增加可能会有X组件,Y组件等等,那么X组件负责提供跳转到X组件的方法,Y组件负责提供跳转到Y组件的方法,并且这些方法,都要经由common组件来管理。

具体代码实现:

1.各个组件应该提供跳转到自己的方法,供别的组件调用
具体如何实现呢?对于A组件的跳转来说,首先,common组件要给别的组件提供一个接口,用于跳转到A组件。
这个接口写在common组件里:

/*** 安装用户组件对外暴露的接口*/public interface IUserInstallService {// 跳转到安装用户页面,其中extra是要传递的数据void launch(Context context, String extra);
}

然后在A组件里,实现此接口(当然A组件的gradle文件得添加对common组件的依赖):

public class UserInstallService implements IUserInstallService {@Overridepublic void launch(Context context, String extra) {Intent intent = new Intent(context, UserInstallActivity.class);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.putExtra("extra_data", extra);    // 传递数据context.startActivity(intent);}
}

这样,跳转方法就已经暴露出来可以供其他组件调用了,那在B组件里,我们怎么调用呢?得想办法拿到UserInstallService 的实例,然后就能调用它的launch方法来跳转了。

因为common组件还要管理其它组件的跳转,所以这些跳转得统一管理起来:

2.对组件间的跳转进行统一管理
我们在common组件里写一个工厂类,用于分配这些跳转:

public class ServiceFactory {private static ServiceFactory instance;//单例模式private ServiceFactory() {}public static ServiceFactory getInstance() {if (instance == null) {synchronized (ServiceFactory.class) {if (instance == null)instance = new ServiceFactory();}}return instance;}// 安装用户组件的跳转服务的注册和获取private IUserInstallService mIUserInstallService;public IUserInstallService getIUserInstallService() {return mIUserInstallService;}public void setIUserInstallService(IUserInstallService mIUserInstallService) {this.mIUserInstallService = mIUserInstallService;}
// 其他组件的跳转服务的注册和获取,与A组件的一样
}

那么很显然,在B组件里,我们就要想办法通过common组件的ServiceFactory 的getLoginService()方法来获取A组件的UserInstallService 的实例。要get,就首先要set,否则拿到的是一个null对象。那么这个set应该放在哪里实现呢?

3.利用Java反射将跳转服务进行实例化
set UserInstallService 对象的操作肯定得放在跳转之前,即get之前。最好在B组件初始化的时候就完成set,而且这个set 操作得放在A组件里,要不然又产生依赖了。

有了这个思路,就可以实现如下:
在common组件里,再增加一个接口:

public interface IAppComponent {public void initialize(Application app);
}

这个接口用来表示各个组件的初始化。各个组件都要重写自己的Application,实现这个接口。比如A组件重写的Application类如下:

public class OrgApp extends Application implements IAppComponent{@Overridepublic void onCreate() {super.onCreate();initialize(this);}@Overridepublic void initialize(Application app) {ServiceFactory.getInstance().setIUserInstallService(new UserInstallService());}
}

如果想让A组件的用于组件单独运行时,需要在A组件的AndroidManifest.xml里,指定这个类为组件的application:(注:作为module运行时记得删除android:name=".OrgApp",不然程序会报错。)

<applicationandroid:name=".OrgApp"android:allowBackup="true"android:label="@string/app_name"android:theme="@style/AppTheme">

但是别忘了,当组件单独运行时,Application类的onCreate()可以走到;而A组件作为module集成之后,Application类是不会被加载的。

怎么办呢?因为app壳组件的Application是肯定会被加载的,所以可以在这里,用反射来加载其他组件的Application类。这也是为什么要在common组件里新建IAppComponent 接口类的原因。app壳组件的Application类也要重写并在manifest里指定。在app壳组件的Application初始化时,可以对其他的组件进行挨个加载,这样,上面我们想要的set 操作就可以在这里完成了。

为了管理要加载的组件,我们在common组件里新建一个AppConfig类,如下;

public class AppConfig {public static String[] COMPONENTS = {"mod.activity.com.orginfo.application.OrgApp",//这个是A组件的Application类// 还有其他的组件的Application类的全名,也都放这里"mod.activity.com.login.application.LoginApp"};
}

上面这个AppConfig类用来记录所有的组件的Application类的全名。
下面是app壳组件的新Application类:

public class AppApplication extends Application implements IAppComponent {@Overridepublic void onCreate() {super.onCreate();initialize(this);}@Overridepublic void initialize(Application app) {// 遍历所有的组件的Application类,依次用反射的方式实例化for (String component : AppConfig.COMPONENTS) {try {Class<?> clazz = Class.forName(component);Object object = clazz.newInstance();// 实例化后,调用各个组件的 set 方法if (object instanceof IAppComponent) {((IAppComponent) object).initialize(app);}} catch (Exception e) {e.printStackTrace();}}}
}

经过这样的处理,上面第二步最后“该在哪里set 跳转服务”的问题就解决了。总结本篇实现的组件间跳转原理如下:

在应用启动的时候,它的APP壳组件的Application类会进行初始化,在这里我们通过反射的方式初始化了其他各组件的Application类,而各组件的Application类在初始化时,又会通过set操作把自己的跳转服务注册到common组件。这样的话,通过common组件get对应的服务,即可实现跳转。

我们来试一下,在B组件里,跳转到A组件,就可以这么写了;

// 安装用户
ServiceFactory.getInstance().getIUserInstallService().launch(mContext, "");

这样B组件就不依赖A组件也可以进行跳转,实现了我们的期望。

还有一点,要是A组件没有被集成到app里,那么ServiceFactory.getInstance().getIUserInstallService()就是null,会报空指针异常。我们需要把getIUserInstallService()补充下:

public IUserInstallService getIUserInstallService() {if (mIUserInstallService == null)mIUserInstallService = new EmptyUserInstallService();return mIUserInstallService;}

这里EmptyUserInstallService是对IUserInstallService 接口的一个空实现,目的只是为了避免这个空指针异常,就不贴代码了。

方法2:
采用路由来实现页面跳转,比较成熟的有:

美团的WMRouter:

阿里的ARouter:

常规业务模块间的数据通讯

使用EventBus实现module间的通讯

组件化项目的混淆方案

组件化项目的Java代码混淆方案采用在集成模式下集中在app壳工程中混淆,各个业务组件不配置混淆文件。集成开发模式下在app壳工程中build.gradle文件的release构建类型中开启混淆属性,其他buildTypes配置方案跟普通项目保持一致,Java混淆配置文件也放置在app壳工程中,各个业务组件的混淆配置规则都应该在app壳工程中的混淆配置文件中添加和修改。

之所以不采用在每个业务组件中开启混淆的方案,是因为 组件在集成模式下都被 Gradle 构建成了 release 类型的arr包,一旦业务组件的代码被混淆,而这时候代码中又出现了bug,将很难根据日志找出导致bug的原因;另外每个业务组件中都保留一份混淆配置文件非常不便于修改和管理,这也是不推荐在业务组件的 build.gradle 文件中配置 buildTypes (构建类型)的原因。

注意事项:
1、需要跨module使用的依赖需要用api,而不是implementation,否则common模块引入库其他模块使用不了。
2、其他module想作为APP单独运行时,需要修改module所在的build.gradle:

application属性,可以独立运行的Android程序,也就是我们的APP;

apply plugin: ‘com.android.application’ 

library属性,不可以独立运行,一般是Android程序依赖的库文件

apply plugin: ‘com.android.library’ 

更多后续欢迎关注,有任何建议可以私信沟通,期待与你的交流。(还有哪些奇葩问题也欢迎沟通吐槽)

android 组件化_你曾遇到的某大厂奇葩问题:Android组件化开发,组件间的Activity页面跳转...相关推荐

  1. python处理视频动漫化_用Python实现抖音上的“人像动漫化”特效,原来这么简单...

    原标题:用Python实现抖音上的"人像动漫化"特效,原来这么简单 作者 | 黄伟呢 来源 | 数据分析与统计学之美 前几天,女友拉着我和她玩儿抖音,就是这个 人像动漫化的操作,顿 ...

  2. 空间金字塔池化_回顾语义分割—DenseASPP (密集空洞空间金字塔池化)

    引言 在分辨率大,分割目标尺度范围广的语句分割任务中,长距离的上下文信息以及不同尺度的信息对于分割结果十分重要.所以为了增大卷积但感受野,常常对提取的feature map进行池化以达到感受野增大的效 ...

  3. java android长连接_基于Java Socket的自定义协议,实现Android与服务器的长连接(一)...

    一.基础知识准备 在正式给大家介绍自定义协议之前,我们先对网络传输和协议解析的相关知识点做一个基本的介绍,尽管这些知识点我们在学校里学过,但难免会有所遗忘,这里先做一个简单的介绍,以便对后文的内容理解 ...

  4. 微信小程序怎么把获取的值传到引用组件内_微信小程序如何将接口获取的数据传递给自定义组件...

    2019-07-11 回答 不知道你是什么意思.帮你改了下 class program { static int n = 4; int i, m; dsd[] a = new dsd[n]; publ ...

  5. 【Visual Studio】Visual Studio 2019 创建 Windows 控制台程序 ( 安装 ‘使用 C++ 的桌面开发‘ 组件 | 创建并运行 Windows 控制台程序 )

    文章目录 一.安装 C++ 桌面开发组件 二.创建并运行 Windows 控制台程序 一.安装 C++ 桌面开发组件 打开 Visual Studio Installer , 点击 " 修改 ...

  6. 开发微信小程序,常用的开发组件有哪些?

    随着微信小程序开发的持续升温,小程序开发也变得越来越流行,因为小程序不仅能帮助企业解决推广的问题,还能为企业带来可观的收益.但是很多企业并不知道如何开发微信小程序,而市面上的开发组件又有很多种,不知道 ...

  7. android pod 组件化_使用 Pod 实现私有模块化管理(组件化 Pods 实现方案)

    概况 众所周知组件化是个好东西,它把项目拆分成多个模块,让每个模块能够独立出来解除各个模块之间的耦合性,作为每个独立的模块不仅仅能够使用组合的方式去组建各个不同的功能组合(前提是各个组件划分的颗粒度只 ...

  8. 【Android 组件化】路由组件 ( 页面跳转参数依赖注入 )

    文章目录 一.参数自动注入 二.自定义注解 三.使用 @Extra 自定义注解 四.注解处理器解析 @Extra 自定义注解 并生成相应 Activity 对应代码 五.博客资源 组件化系列博客 : ...

  9. swift 组件化_打造完备的iOS组件化方案:如何面向接口进行模块解耦?

    作者 | 黑超熊猫zuik,一个修行中的 iOS 开发,喜欢搞点别人没搞过的东西,钻研过逆向工程.VIPER 架构和组件化. 关于组件化的探讨已经有不少了,在之前的文章 iOS VIPER架构实践(三 ...

最新文章

  1. CSS 外补白(Margin) 内补白(Padding) 边框属性 定位(positioning)属性 布局(layout)属性
  2. MySQL create table as与create table like对比
  3. iOS沙盒目录结构解析
  4. php使用redis持久化,redis如何持久化
  5. C++工作笔记-对结构体的进一步认识
  6. 十大排序算法:冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序、计数排序、桶排序、基数排序
  7. ShardingSphere Raw JDBC 主从示例
  8. citirx for wincor configuration (citrix 7.5 setup with WI)
  9. XML Schema 简介
  10. ASP.NET开发框架之HIPPO技术内幕(三)--数据库连接
  11. let var const的区别
  12. 步进电机驱动实验(89C51 + KEIL + Proteus)
  13. 一点一滴开始搭建自己的项目框架之支付宝篇 支付宝提现
  14. 如何在php网站上插入站长统计,如何为自己的Wordpress网站添加站长统计代码 | 泰泰博客...
  15. python查看微信撤回消息怎么弄_微信撤回消息怎么查?Python3步啥都知道了!
  16. 【干货】游戏开发团队部门岗位
  17. java抠图人物背景图片_如何进行人物抠图?让你快速完成复杂背景人像的在线抠图...
  18. iframe简单使用
  19. i7 13700k和i7 12700k差距
  20. Pool thread stack traces: Thread[C3P0PooledConnectionPoolManager[identityToken-原因解决办法

热门文章

  1. 注意升级Oracle 19c:SE2标准版不再支持RAC
  2. ACOUG 联合创始人盖国强:万象更新,数据库技术和生态的发展演进
  3. 专访福建移动林志云: 5G使能,运营商全面进入数字化转型之路
  4. 撬动百亿VRAR产业,让VR们“造”起来
  5. Redisson:这么强大的实现分布式锁框架,你还没有?
  6. 只需6步,教你从零开发一个签到小程序
  7. 做开发,这几种锁机制你不得不了解一下
  8. SecSolar:为代码“捉虫”,让你能更专心写代码
  9. 实践案例丨Pt-osc工具连接rds for mysql 数据库失败
  10. LiteOS调测利器:backtrace函数原理知多少