原文首发于微信公众号:躬行之(jzman-blog),欢迎关注交流!java
当 App 项目复杂必定的程度,将项目组件化是必不可少的,组件化能够更好的进行功能的划分,提到组件化有人可能会想到模块化,其实组件化和模块化的本质是同样的,都是为了代码重用的业务解耦,模块化主要按照业务划分,而组件化主要按照功能划分,从组件化最基础的几个方面打开组件化的大门。android
组件化中两个功能模块时不直接依赖的,其依赖规则是经过 Base module 间接依赖,当组件之间的 Activity 进行界面跳转时,因为没有相互依赖的关系,每每会没法引用另外一个 module 中的 Activity。编程
隐式跳转是经过 Android 原生 Intent 匹配机制来实现相应跳转,就是使用 Action 来跳转到对应的 Activity,这样使用隐式跳转的方式就能够跨 module 实现 Activity 之间的跳转了,注意一点,若是移出 Activity 所在的 module 而不移出相应的跳转,若是继续跳转会出现异常,使用隐式 Intent 跳转须要验证是否会接收该 Intent,须要对该 Intent 对象调用 resolveActivity() 方法来判断至少有一个应用可以处理该 Intent,经过隐式跳转的方式还能够设置 exported 为 false 来确保只有本身的 App 才可以启动对应的组件。微信
在 Android 开发中可将 module 当作不一样的网络,而对应的 Router 就是链接各个 module 的中转站,这个中转站能够对页面跳转的参数等进行统一处理,ARouter 是阿里开源出来的一个页面跳转路由,使用 ARouter 能够替代隐式跳转来完成不一样 module、不一样组件之间的跳转以及跳转过程的监听、参数的传递等,ARouter 支持路径跳转和 URL 跳转两种方式,使用也很是灵活,ARouter 的具体使用这里不作介绍,其具体使用会在单独一篇文章中详解,ARouter 与 Android 传统跳转方式的对好比下:网络
组件化开发中最重要的一点就是各个模块、各个组件之间要尽量解耦,这样很容易就会想到使用 Java 中的反射机制,使用反射可在运行状态下获取某个类的全部信息,而后就能够动态操做这个类的属性和方法了。若是 Fragment 单独做为一个组件来使用时,当这个 Fragment 组件不须要被移出后,若是是常规的 Fragment 则会由于索引不到该 Fragment 而使得 App 崩溃,想一下若是使用反射建立 Fragment 的方式则至少不会引发 App 崩溃,这里能够捕捉异常完成相关逻辑,这样是否是下降了耦合呢。可见,虽然反射有必定的性能问题,但使用反射确实能在必定程度上下降耦合,学习组件化 Java 反射机制应该是必须的一部分。app
组件化开发中要求每一个组件都能独立运行,通常状况下每一个组件都有必定的初始化步骤,最好的一种状况是项目须要的几个组件的初始化基本相同,那就可将初始化放在 BaseModule 中进行统一初始化,可是这种状况毕竟比较理想,通常状况是每一个组件的初始化都不同,可能你会想到在各自的 Application 初始化,若是在各自的 Application 中初始化,当在最终编译因为 Application 的合并不免会出一些问题,这种方式也不可取,到这里又想到了反射,在各组件中建立初始化文件,而后在最终的 Application 中经过反射完成各个组件的初始化操做,这里经过 Java 的反射机制完成了组件化开发中 Application 的动态配置。模块化
组件化开发过程当中,若是 ModuleA 的 AmdroidManifest 文件中使用 android:name 属性指定了相应的 Application,而主 App Module 的 AndroidManifest 文件中也使用 android:name 属性指定了相对应的 Application,此时就必须在 主App Module 的 AndroidManifest 文件中使用 tools:replace="android:name" 来解决冲突,使用 replace 属性表示该属性也就是在 标签下的 android:name 属性可在编译过程当中被替换,这样根据 AndroidManifest 文件替换规则最终指定的 Application 应该是 App Module 中的指定的 Application。组件化
举一个例子,我在项目中的某个功能 Module 中使用 SMSSDK 来完成短信验证的功能,由于其余地方不用,因此只引入到了要使用的功能 Module 中,若是其余 Module 会使用应该将 SMSSDK 引入到 BaseModule 中,使用 SMSSDK 若是不指定该 Module 的 Application,MobSDK 会将 com.mob.MobApplication 指定为该 Module 的 Application,此时在总体编译打包时就会出现 AndroidManifest 文件的 android:name 属性冲突,固然了解决方法就是使用 replace 属性了。 AndroidManifest 文件合并后的主要冲突也就是这个问题了,固然 下的其余属性有冲突,也是使用 replace 属性。在实际的开发中多验证会更有收获喔。性能
组件化开发中另外须要注意的一点是防止资源名称同样致使最终合并的时候,由于冲突形成资源引用错误或者某些资源丢失等,如字符串、颜色值等资源等合并的时候会被后面加载的相同名称的资源所替换,解决的思路是在资源命名上要有必定的规则,能够在 build.gradle 文件中配置 "resourcePrefix "组件名称"" 的方式强制约束开发者确保资源名称惟一,建议 Module 中资源的命名格式为 "Module名称功能其余"。学习
组件化开发中,最终合并时每一个组件都是以 Lib Module 的形式存在,而 Lib Module 中 R.java 文件中定义的静态变量没有声明为 final,这就意味着不能在组件 Module 中使用相对应的常量了,如在时候 switch 语句就不能使用了,这就要求在组件中要使用 if 语句来替代 switch 语句,固然在组件独立运行的时候是没有这个问题的。
开发中常常会使用到 Butterknife,Butterknife 可很是方便的对 View 及 View 的事件等进行注解操做,它采用的是编译时注解机制,注解中只能使用常量,因此在 Butterknife 在组件化开发中应该使用 R2 代替 R,R2 其实是 R 的拷贝, R2 对应声明的变量是 final,因此在组件化开发中若是使用 Butterknife 在相应的注解中要使用 R2 替代 R。