问题
1、单一工程的业务模块耦合度太高,不能快速适应快速变化的业务需求
2、进行简单修改也必须花4、5分钟重新打包编译整个程序
3、功能测试和系统测试每次都要进行
4、团队协同开发存在较多的冲突,需要花更多时间去协调;
5、不能灵活的对业务模块进行配置和组装;
方案
项目组件化,将不同模块分成不同组件。
代码分“组件”
App壳工程:负责管理各个业务组件,和混淆、打包apk,不执行具体业务
业务组件:根据公司具体业务而独立形成一个的工程
功能组件:提供开发APP的某些基础功能,例如图片加载、线程池管理、打印日志等
开发分“模式”
集成开发模式:此模式下,App壳工程囊括所有组件组成一个 App,业务组件作为library存在
组件开发模式:此模式下,每个业务组件工程能最为 App 独立运行
建议:
App壳工程不进行任何具体业务,使用业务组件 Main 来实现APP启动页面、主界面等业务
业务组件之间不要相互依赖,相互之间的 Activity 调用借助 ActivityRouter 来实现
共用的功能组件,用来管理全程序都需要功能和对象基类。如:Application 基类。
流程
在 Android Studio 借助 Gradle 自动构建工具实现项目组件化。
组件模式、继承模式转换开关
在项目根目录的文件 gradle.properties 增加常量isDebug
isDebug = true
每个组件工程借助开关来转换工程属性。编辑组件的build.gradle
// 组件开发模式,作为一个单独的应用
if (isDebug.toBoolean()) {
apply plugin: 'com.android.application'
}
// 继承开发模式,作为一个library
else {
apply plugin: 'com.android.library'
}
多组件 Manifest 合并
在集成开发模式下,所有的 AndroidManifest.xml 都要合并在壳工程中,如果将每个组件的 Application 和 LaunchActivity 和在一起会产生冲突。
业务组件需要两个 AndroidManifest.xml,一个集成开发模式用,一个组件开发模式时使用。
sourceSets {
main {
// 组件开发模式
if (isDebug.toBoolean()) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
}
// 集成开发模式
else {
manifest.srcFile 'src/main/AndroidManifest.xml'
// 排除 debug 文件夹中的所有 Java 文件
java {
exclude 'debug/**'
}
}
}
}
业务组件 单独运行时,需要获取数据和初始化服务,需要自己的 Application 等类,但在作为 Library 时,又不需要这些类。
建立 debug 包,将组件开发模式下才需要的类放在其中,并在集成开发模式时排除。
因为最后会排除,所以业务组件不能持有 debug 保重对象的强引用。
组件开发模式下的 AndroidManifest.xml,要声明 Application 和 LaunchActivity,并且设置 App 的名字、图标等属性
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.module.main">
<application
android:name=".debug.Application4Main"
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true"
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>
集成开发模式下的 AndroidManifest.xml,为了方便声明此组件下所有 Activity 的主题外,其他的不用声明
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.module.main">
<!-- 不能设置 Application 、名称、图标等属性,避免与壳工程重复 -->
<!-- 业务组件:不能设置 Launch Activity-->
<application android:theme="@style/AppTheme">
<!--
如果是 Main 组件
声明 Launch Activity 作为整个程序的 Launch Activity
-->
<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>
资源合并
图片等资源名重复会报错,如:A组件和B组件都ic_back.png
的文件,在集成开发模式下回编译出错。
最简单的解决办法就是在项目中约定资源文件命名约束,如以组件名为资源名开头。
全局Context
Android 程序启动后会建立且只有建立一个 Application 对象。在组件开发模式下,肯定使用了正对组件声明的 Application,切换为集成开发模式后,所有的组件都使用 App 壳工程建立的 Application 对象。在组件开发模式下,肯定使用了正对组件声明的。
为了不出问题,要保证业务组件在集成开发模式下也能正确获取到 Application。
建立 BaseApplication ,业务组件工程和App壳工程声明的 Application 都继承自 BaseApplication 就行了。
组件间调用
业务组件A 想调起 业务组件B 的 Activity,肯定不能让 A 导入 B 的依赖,因为项目组件化的目的就是解决模块之间的强依赖问题。
1.) 使用Uri来进行跳转,Activity声明设置
<activity android:name=".MainViewActivity">
<intent-filter>
<!-- 为了明确的指向性,这里可设置专属action,如:android.tryout.action.MainViewActivity -->
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<!-- 支持 JS 调起界面 -->
<category android:name="android.intent.category.BROWSABLE" />
<data
android:host="view.main"
android:scheme="myscheme" />
</intent-filter>
</activity>
在程序捏调起此界面
Uri uri = Uri.parse("myscheme://view.mian");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
需要注意的是,使用URL启动Activity默认是FLAG_ACTIVITY_NEW_TASK启动模式,所以可能存在URL启动的Activity跟应用已启动的Activity不再同一个堆栈的现象。
需要我们在manifest中将Activity多配置一个taskAffinity属性,约束URL启动的Activity与应用自身的启动的Activity在同一个堆栈中。
2.) 可使用 ActivityRouter 框架,使用 RUL 来进行 Activity 的跳转
ActivityRouter Github
好处
不限制开发框架(MVP、MVC、MVVM),可自由选择
方便单元测试
代码模块耦合降低,降低项目的维护难度
加快编译速度,提高开发效率
团队协同开发冲突降低
为公司节省了经营成本