前言

关于什么是组件化、为什么要进行组件化以及实施组件化的基本流程网上一搜一大把,这里不做过多说明,不了解的话可以Google一下。这里主要记录一下组件化开发的一些心得和踩的一些坑。

先看一下项目结构图

结构很简单,有一个公共的基础module类commonlibrary来处理一些公共的东西,比如第三方库的依赖,基类封装,工具类等。中间层是各个独立的业务模块,各个模块之间互相隔离。最下面是app的壳,主要配置签名打包什么的。具体可以看一下demo。

组件化实施步骤

1、设置module是否作为组件的开关

在gradle.properties文件里定义一个常量IsBuildApp = false,表示是否把组件module作为单独的app运行。定义好了这个常量后,在项目的任何一个gradle文件里都可以读取到这个值,那么就用这个值来作为module组件是否需要单独运行的开关。

// 在module组件的gradle里配置如下,gradle.properties 中的数据类型都是String类型,这里需要做一下转换
if (IsBuildApp.toBoolean()){apply plugin: 'com.android.application'
}else {apply plugin: 'com.android.library'
}
复制代码
2、组件module的清单文件AndroidManifest合并问题

我们知道android的四大组件、权限等都是需要注册的,当module单独运行的时候,肯定需要一个清单文件注册组件和申请权限,但是当module作为app的一个子组件存在的时候,清单文件是要合并到app的壳工程中的,这个时候如果每个module都有自己的启动页面和自定义application的话,就会引起冲突。

为了解决这个问题,那就需要根据module是否需要单独运行来配置不同的清单文件。在java同级目录新建independent目录,在此目录下创建项目module需要单独运行的清单文件和application。然后在module的gradle文件里指定清单文件路径,代码如下:

// 在android领域里指定清单文件的路径sourceSets {main {if (IsBuildApp.toBoolean()) {// 单独作为app运行的清单文件,这里可以添加启动页面、自定义application等。manifest.srcFile 'src/main/independent/AndroidManifest.xml'} else {// 作为组件的清单文件manifest.srcFile 'src/main/AndroidManifest.xml'//release模式下排除independent文件夹中的所有Java文件java {exclude 'independent/**'}}}
}
复制代码

这样配置完成以后,作为组件的清单文件是不能有自己的启动页面、application、appname等属性的,下面看一下完整的配置:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.article.demos.vue"><application android:theme="@style/AppTheme"><activity android:name=".ui.VueActivity" /></application>
</manifest>
复制代码

下面看一下独立运行模式下的清单文件:

// 作为独立app运行的清单文件,注意这里我设置了主题,不然的话会报错。<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.article.demos.main"><application android:theme="@style/AppTheme"><activity android:name=".MainActivity"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>
复制代码

独立运行的话,就和正常的app清单文件一样,要有启动页面,application标签可以添加label、icon、自定义application等,就不多说啦。

3、全局Application的问题

在commonlibrary中创建自定义application,因为其他的module都依赖这个module,所以其他的module都可以获取到这个全局的application。另外,组件在独立运行模式下的application,继承我们自定义这个BaseApplication就可以了。因为我们在release模式下,排除了所有independent文件夹下的java文件,所以作为组件运行时,并不会产生application的冲突,配置如下:

sourceSets {main {if (IsBuildApp.toBoolean()) {manifest.srcFile 'src/main/independent/AndroidManifest.xml'} else {manifest.srcFile 'src/main/AndroidManifest.xml'//release模式下排除independent文件夹中的所有Java文件java {exclude 'independent/**'}}}
}
复制代码
4、重复依赖三方库的问题

为了避免重复依赖三方库的问题,我们的三方库依赖统一放在commonlibrary的module中,这样既可以避免重复依赖,又方便管理。然后我们在app的module里,如下引用即可:

dependencies {implementation fileTree(include: ['*.jar'], dir: 'libs')if (IsBuildApp.toBoolean()) {implementation project(':commonlibrary')} else {implementation project(':androidmodule')implementation project(':vuemodule')implementation project(':kotlinmodule')implementation project(':javamodule')}
}
复制代码
5、资源冲突问题

资源冲突主要是指各个module里的资源文件名冲突的问题,如果命名一样,合并的时候便会产生冲突。

解决冲突主要有两个解决方案,一个是约定规则,比如资源名约定都以module名开头。

方案二是通过gradle脚本来设置,在各个组件的gradle文件里添加如下代码:

resourcePrefix "module名称_"
复制代码

但是这种配置有限制,比如只能限定xml里的资源,所以并不推荐这种方式。

6、组件间跳转

因为组件是相互隔离的,我们并不能显式跳转,这里我们选用阿里巴巴的Arouter路由跳转,项目的地址github.com/alibaba/ARo…。

这里需要特别说明一下,需要跳转的目标module需要引入arouter的注解处理器,否则无法处理router注解会出现路径不匹配的问题:

annotationProcessor 'com.alibaba:arouter-compiler:1.1.4'
复制代码

同时,改module的defaultconfig里也别忘记配置moduleName

javaCompileOptions {annotationProcessorOptions {arguments = [ moduleName : project.getName() ]}}
复制代码
7、跨module交互

跨moduel交互一般是指module间通信和module间的相互调用。module间通信这里选用eventbus,很简单,就不过多说明了。

下面说一下同级module直接的通信,比如我在任何一个页面要调用loginModule里的微信登录方法,因为各个module是互相独立的,互不依赖,想要直接调用基本不可能。目前网上发现有两种解决方案,一个是写一个反射工具类,通过反射获取到要调用的类,然后调用相应的方法。另一个是通过commonModule做一下桥接,了解更多可以参考这里。不过感觉用Arouter能更优雅的实现,下面具体讲一下利用arouter来实现。

首先,在公共module里创建一个接口IService

public interface IService extends IProvider{String wxLogin();
}
复制代码

接口里定义一个微信登录的伪代码,然后在我们的登录组件里,实现该接口并添加route注解

@Route(path = Constant.WX_LOGIN)
public class WxTest implements IService{@Overridepublic void init(Context context) {}@Overridepublic String wxLogin() {return "wxlogin";}
}
复制代码

其中 Constant.WX_LOGIN是我定义的一个字符串常量

public static final String WX_LOGIN = "/wx/login";
复制代码

以上两步就把工作做完了,下面只需要在需要调用的页面调用登录就行了。首先,我们获取到IService

/*** 推荐使用方式二来获取IService*/// IService iService = (IService) ARouter.getInstance().build(Constant.WX_LOGIN).navigation();IService iService = ARouter.getInstance().navigation(IService.class);
复制代码

拿到IService后,就可以放心大胆的调用登录方法就行了。

mBinding.btLogin.setOnClickListener(v -> {String s = iService.wxLogin();Toast.makeText(getContext(), s, Toast.LENGTH_SHORT).show();});
复制代码
8、fragment的组件化

一般的项目首页都是一个activity和多个fragment组成。由于组件间的隔离,我们在首页里怎么获取到其他组件里的fragment呢?开篇的两个参考文章分别使用了两种不同的方式,有兴趣的朋友可以看看。各有利弊吧,一个是查询所有,太耗时。一个是直接反射获取,但是好像有点违背组件隔离,需要知道fragment的全路径。

这里我参考了《Android组件化架构》一书,使用arouter来获取。其实三种方式获取的原理一样,都是通过反射。我们看一下arouter的注解的源码就知道:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {……}
复制代码

可以看到Route注解的retention是CLASS,也是通过反射来获取。

9、遇到的一些坑

(1)使用dataBinding的话,每个module的gradle文件里都要加上dataBinding的支持,否则无法生成相应的binding类

// 每个module都加上dataBinding的支持,否则无法生成相应的binding类dataBinding {enabled = true}
复制代码

(2)java8的支持一样要每个module都要单独配置

compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8
}
复制代码

(3)升级到as 3.1.2后,出现无法访问TaskStackBuilder的问题

检查一下你的support包,将你的support包更新到27或以上即可。

(4)如果使用有自定义注解annotation的话,如果编译报错 Annotation processors must be explicitly declared now...,那么在commonlibrary的gradle文件的defaultConfig里添加如下代码:

// Annotation processors must be explicitly declared nowjavaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
复制代码

(5)如果你组件化开发,子module中无法使用butterknife的话,网上自行搜解决方案吧(?‍♀️)

关于为何出现这个问题,推荐一篇博文R.java、R2.java是时候懂了

(6)其他问题本篇博客会持续更新……

2018年6.15更新………………………………………………………… 编译报错

 Multiple dex files define Lcom/alibaba/android/arouter/routes/ARouter$$Group$$module
复制代码

一般网上说是依赖版本冲突,其实这个问题是不同module之间有相同分组导致的问题,比如a模块 path = "/message/a",b模块 path = "/message/b",有相同的message分组,修改成不一样的就可以了。

2018.8.20更新…………………………………………………………

最近在用kotlin和java混合开发,发现原有java页面跳转新写的kotlin页面 arouter 页面跳转的时候报异常提示 There is no route match the path……,此时参考官方文档即可解决,

// 在kotlin的module中添加插件
apply plugin: 'kotlin-kapt'
// 依赖里 使用kapt 引用
dependencies {compile 'com.alibaba:arouter-api:x.x.x'kapt 'com.alibaba:arouter-compiler:x.x.x'...
}
复制代码
2018.8.22更新…………………………………………………………

遇到了一个很蛋疼的问题,在纯java写的module里通过arouter跳转到另一个module里的kotlin页面的时候,发现setcontentview方法无效,页面什么都不显示。调试了半天,发现是页面的xml布局文件和一个空的xml布局文件重名了,导致kotlin页面加载了空页面的布局,在此记录一下,好尴尬。

最后附上完整的demo地址,如果对你有帮助麻烦start鼓励一下,你的鼓励是我前进的动力。

项目实战之组件化架构相关推荐

  1. 【厚积薄发系列】C++项目总结19—组件化架构思想

    在一个大型的项目中,随着业务不断拓展,开发人员和代码量的不断增加,传统的单体架构会经常遇到以下问题: 1.业务模块划分不够清晰,模块之间高度耦合,修改和新增需求的时候可能会导致牵一发而动全身的雪崩. ...

  2. 单文件组件的组件传值_移动端组件化架构(下)

    我的组件化方案 对于项目架构来说,一定要建立于业务之上来设计架构.不同的项目业务不同,组件化方案的设计也会不同,应该设计最适合公司业务的架构. 架构设计 以我之前公司项目为例,项目是一个地图导航应用, ...

  3. 案例精选 | 蘑菇街、滴滴、淘宝、微信的组件化架构解析

    导读:前段时间公司项目打算重构,准确来说应该是按之前的产品逻辑重写一个项目.在重构项目之前涉及到架构选型的问题,我和组里小伙伴一起研究了一下组件化架构,打算将项目重构为组件化架构.当然不是直接拿来照搬 ...

  4. vue2.0 class声明组件_案例精选 | 蘑菇街、滴滴、淘宝、微信的组件化架构解析

    导读:前段时间公司项目打算重构,准确来说应该是按之前的产品逻辑重写一个项目.在重构项目之前涉及到架构选型的问题,我和组里小伙伴一起研究了一下组件化架构,打算将项目重构为组件化架构.当然不是直接拿来照搬 ...

  5. 蘑菇街、滴滴、淘宝、微信的组件化架构解析,附Demo和PDF

    前段时间公司项目打算重构,准确来说应该是按之前的产品逻辑重写一个项目?.在重构项目之前涉及到架构选型的问题,我和组里小伙伴一起研究了一下组件化架构,打算将项目重构为组件化架构.当然不是直接拿来照搬,还 ...

  6. iOS开发之组件化架构漫谈

    前段时间公司项目打算重构,准确来说应该是按之前的产品逻辑重写一个项目.在重构项目之前涉及到架构选型的问题,我和组里小伙伴一起研究了一下组件化架构,打算将项目重构为组件化架构.当然不是直接拿来照搬,还是 ...

  7. 天画-codeMaker组件化架构升级实践

    神帅 读完需要 11 分钟 速读仅需 4 分钟 1 背景 1.1 背景说明 在两个月前我扩展了基于调用时序的代码生成,将代码生成的粒度从代码方法级别提升到了代码行级别,从整个迭代过程来看也逐步积累了一 ...

  8. 得到、微信、美团、爱奇艺APP组件化架构实践

    一.背景 随着项目逐渐扩展,业务功能越来越多,代码量越来越多,开发人员数量也越来越多.此过程中,你是否有过以下烦恼? 项目模块多且复杂,编译一次要5分钟甚至10分钟?太慢不能忍? 改了一行代码 或只调 ...

  9. 如何通过 Vue+Webpack 来做通用的前端组件化架构设计

    目录:   1. 架构选型     2. 架构目录介绍     3. 架构说明     4. 招聘消息 目前如果要说比较流行的前端架构哪家强,屈指可数:reactjs.angularjs.emberj ...

最新文章

  1. 参考别人博客,自己实现用idea运行eclipse项目--学生管理系统-
  2. 你可能不知道的Docker资源限制
  3. 萌新向Python数据分析及数据挖掘 第三章 机器学习常用算法 第四节 PCA与梯度上升 (上)理解篇...
  4. echarts加载动画效果_入门ae教程:科技类的加载动画,非常酷炫的效果,附带教程...
  5. php保存流文件到本地,php下载保存文件保存到本地的两种实现方法
  6. 如何在Ubuntu 18.04上安装Django
  7. SpringMVC注解@RequestParam解析
  8. 会声会影滤镜特效教程之气泡滤镜
  9. mysql给数据库表批量加表前缀_批量修改mysql数据库表前缀。
  10. 富士急乐园免税店将开业,打造游园购物访日体验
  11. 80004005错误代码_WIN7错误代码0×80004005图文解决教程
  12. java bean vo_关于JavaBean和vo的解释
  13. 剪贴板增强工具CLCL
  14. 联想g510升级方案_联想G510更换固态、加装内存条、移机械硬盘至光驱位简记
  15. 自动释放池 ARC机制
  16. 美团CTO罗道锋离职 王慧文将兼任基础研发平台负责人
  17. replace()方法
  18. 衡水启动智能公交系统
  19. IDEA搭建简单的SSM(spring+springMVC+mybatis)框架
  20. VScode 使用background插件 报错:Code 安装似乎损坏。请重新安装 解决方法

热门文章

  1. 深入理解 JavaScript Function
  2. c#发送http请求
  3. 在多个游戏视图间切换实现效果
  4. 网络营销之CPA、CPS、CPM、CPT、CPC 是什么
  5. 使用Prism提供的类实现WPF MVVM点餐Demo
  6. 蚂蚁移动开发平台 mPaaS 3.0 智能化 + 生态化
  7. 【转】Struts2中转换Date类型的问题
  8. How to Delete and Recreate the _msdcs DNS zone on a Windows DNS Server
  9. 团队项目—后续阶段第一天
  10. 分页功能 (包含增删改查)工具类