组件化框架是指 mPaaS 基于 OSGi(Open Service Gateway Initiative,开放服务网关倡议)技术把一个 App 划分成业务独立的一个或多个 Bundle 工程以及一个 Portal 工程的框架。mPaaS 会对每个 Bundle 工程的生命周期和依赖加以管理,使用 Portal 工程把所有的 Bundle 工程包合并成一个可运行的 .apk
包。
mPaaS 框架适合团队协同开发 App,并且该框架包含组件初始化、埋点等功能,方便您轻松接入 mPaaS 组件。
Bundle 工程
传统的原生工程由一个主模块或是一个主 module 和若干个子 module 组成,而一个 mPaaS Bundle 工程一般由一个名为 app 的主 module 和若干个子 module 组成。
例如,在支付宝中,一个 Bundle 一般由一个名为 app 的主 module 和以下三个子 module 组成:
api:纯代码接口,interface 的定义。
biz:interface 的实现。
ui:activity,自定义 view 等。
至少有一个名为 api 的子 module。如果没有子 module, 就打不出 Bundle 的接口包,并且该 Bundle 不能被其他 Bundle 依赖。
通过阅读本文,您将从以下方面了解 Bundle 工程:
Bundle 与传统工程区别
Bundle 本质上也是一个原生工程,只是在 工程、主 Module、子 Module 的 build.gradle
中多了 mPaaS 的 Apply 插件,具体差别体现在以下方面:
工程根目录
build.gradle
主 module 的
build.gradle
子 module 的
build.gradle
工程根目录 build.gradle
在工程根目录的 build.gradle
中,增加了对 mPaaS 插件的依赖:
因功能迭代,插件版本可能会不断增加。
classpath 'com.alipay.android:android-gradle-plugin:3.0.0.9.13'
主 module 的 build.gradle
在主 module 的 build.gradle
中,增加了 mPaaS Bundle Apply 插件 的声明,表示该工程为 Bundle 工程,Bundle 配置如下:
apply plugin: 'com.alipay.bundle'
主 module 的 build.gradle
中还增加了以下配置:
其中:
version
:该 Bundle 的 version。group
:该 Bundle 的 groupid。exportPackages
: 描述当前 Bundle 工程所有的类在哪些包名下面,包名可以取合集。非静态链接的 Bundle 必须填写exportPackages
,否则会出现类加载不到的问题。例如,如果所有的代码在com.alipay.demo
和com.alipay.bundle
下,那么在exportPackages
中就可以写com.alipay
,也可以写com.alipay.demo
、com.alipay.bundle
。包名不宜过长或过短。initLevel
:框架启动时加载该 Bundle 的时机。时机范围在 0-100,数字越小表示越早加载。其中11110000
为使用时加载,即懒加载。packageId
:描述当前 Bundle 的资源的 ID,供 aapt 打包时需要。由于是多 Bundle 架构,每个 Bundle 的 packageId 必须唯一,不可与其它 Bundle 的 packageId 重复。目前 mPaaS 已经使用的 packageId 如下:
Bundle | packageId |
com.alipay.android.phone.thirdparty:androidsupportrecyclerview-build | 28 |
com.alipay.android.phone.mobilesdk:framework-build | 30 |
com.alipay.android.phone.rome:pushservice-build | 35 |
com.alipay.android.phone.sync:syncservice-build | 38 |
com.alipay.android.phone.wallet:nebulabiz-build | 41 |
com.alipay.android.phone.mobilecommon:share-build | 42 |
com.alipay.android.phone.wallet:nebulacore-build | 66 |
com.alipay.android.mpaas:scan-build | 72 |
com.alipay.android.phone.wallet:nebula-build | 76 |
com.alipay.android.phone.securitycommon:aliupgrade-build | 77 |
在 dependencies
中会添加对 mPaaS 的如下依赖:
dependencies {
compile project(":api")
apt 'com.alipay.android.tools:androidannotations:2.7.1@jar'
//mPaaS dependencies
provided 'com.alipay.android.phone.thirdparty:fastjson-api:1.1.73@jar'
provided 'com.alipay.android.phone.thirdparty:androidsupport-api:13.23@jar'
}
子 module 的 build.gradle
在子 module 的 build.gradle
中,增加了 mPaaS Apply 插件 的声明,表示该工程为 Bundle 的子 module 工程,最终会打出该 Bundle 的接口包。
apply plugin: 'com.alipay.library'
在 dependencies
中会添加对 mPaaS 的如下依赖:
dependencies {
apt 'com.alipay.android.tools:androidannotations:2.7.1@jar'
//mPaaS dependencies
provided "com.alipay.android.phone.thirdparty:utdid-api:1.0.3@jar"
provided "com.alipay.android.phone.mobilesdk:framework-api:2.1.1@jar"
}
Bundle 属性
本框架的 Bundle 属性设计思路参考 OSGi 的 Bundle,但比 OSGi 的 Bundle 更简洁和轻巧。
以下表格列出 Bundle 属性及其解释:
属性 | 解释 |
Bundle-Name | Bundle Name,来自于由 |
Bundle-Version | Bundle Version,来自于 |
Init-Level | Bundle 的加载时机,来自于 |
Package-Id | Bundle 资源的 packageid,来自于 |
Contains-Dex | 是否包含 dex,编译插件自动判断。 |
Contains-Res | 是否包含资源,编译插件自动判断。 |
Native-Library | 包含的 |
Component-Name | 来自于 |
exportPackages | 该 Bundle 的所有的类所在的包名,参考主 module 的 |
Bundle 接口包
一个 Bundle 有可能包含多个 子 Module,如 biz, api, ui。在编译打包 Bundle 的时候,每个子 module 都会分别生成一个格式为 .jar
接口包,其中 api 接口包可以被其他 Bundle 使用。
在打包的同时,也会生成一个 Bundle 工程包,这个工程包包含所有的子 module,工程包可以被 Portal 工程使用,工程包在 Portal 中编译,最后生成 .apk
包。
由 Bundle 的 子 module 打出来的接口包,只对外提供定义的 java/kotlin 接口类,不包含其他如 res 下的资源,且这些接口包仅限于来自名称为 api 的 module。
各 Bundle 工程直接通过 Bundle 的接口包互相依赖,需要在 bundle 的
build.gradle
中的dependency
配置依赖 api 接口。例如,Bundle A 依赖 Bundle B 中的bapi
子 module,那么在 Bundle A 相应的子 module 的build.gradle
中的dependency
配置对bapi
的依赖。provided "com.alipay.android.phone:bundleB:1.0.1:bapi@jar"
依赖中涉及的
groupId:artifactid:version:classifier
分别对应 Bundle 中声明的 group,name,version,子 module 的名字。Bundle 的 name 默认为主 module 的文件夹名,可以在
settings.gradle
中修改,如下代码所示,其中 app 为主 module 的工程名:include ':api', ':xxxx-build' project(':xxxx-build').projectDir = new File('app')
Bundle 工程包
由整个 Bundle 工程打出来的
.jar
包其实是一个.apk
格式的文件,但是也以.jar
结尾,如framework-build.jar
。如果要在 Portal 中依赖 Bundle,则在 Portal 主 module 的
build.gradle
中的dependency
中声明依赖 Bundle 包,如下所示:dependencies { bundle "com.alipay.android.phone.mobilesdk:framework-build:version@jar" manifest "com.alipay.android.phone.mobilesdk:framework-build:version:AndroidManifest@xml" }
对 Bundle 打包分两种:debug 包和 release 包,在 Portal 依赖 Bundle 的 debug 包时,需要在 debug 包中额外加上
:raw
。当 Portal 依赖 Bundle 的 debug 包时,
bundle "com.alipay.android.phone.mobilesdk:framework-build:version:raw@jar"
当 Portal 依赖 Bundle 的 release 包时,
bundle "com.alipay.android.phone.mobilesdk:framework-build:version@jar"
打 Portal 包时,需要确定以下内容:
哪些 Bundle 是要打在 app 的主 dex 中。静态链接,有
ContentProvider
的 Bundle 必须放在静态链接中。哪些动态加载。如果 App 不大,建议都在主 dex 中。
如果想把 Bundle 的代码打进主 dex 中,则需要在 Portal 的
slinks
文件中配置当前 Bundle。配置内容为:groupId-artifactId
。如果以-build
结尾,则去掉 -build。例如, groupId 为com.mpaas.group
,artifactId 为testBundle-build
,则需要在slinks
文件中添加一行:com.mpaas.group-testBundle
。静态链接:把 Bundle 的代码打进
apk
的classes.dex
或者classes1.dex
,classes2.dex
等中,以便工程启动时就可以加载 Bundle 中的类。
Portal 工程
Portal 工程把所有的 Bundle 工程包合并成一个可运行的 .apk
包。
Portal 与传统工程区别
Portal 和传统开发中的工程的区别体现在 build.gradle
:
工程根目录
build.gradle
主 module 目录
build.gradle
工程根目录 build.gradle
如下图所示,classpath 多了一个 com.alipay.android:android-gradle-plugin:2.1.3.2.7
插件 :
因功能迭代,插件版本可能会不断增加。
该插件中包含 Portal 插件,Portal 插件可以在打包过程中把各 Bundle 合并。
合并 Bundle 的
.jar
合并 Bundle 的
AndroidManifest
主 module 目录 build.gradle
多了 mPaaS Apply Portal 插件 的声明,表示该工程为 Portal 工程。Portal 配置如下:
apply plugin: 'com.alipay.portal'
同时,在 dependencies
中添加相应的对 Bundle 的依赖。dependencies
中的语句是 bundle 和 manifest 的声明,用来表示 Portal 依赖了哪些 Bundle 或 manifest:
通常 Portal 下面不写代码。
以下几种在 Bundle 工程中使用的资源(style/drawable/string等),必须放在 Portal 工程中,否则会导致编译/运行时找不到资源:
AndroidManifest.xml
中使用的资源。传递给 NotificaionManager 使用的资源。
通过
getResources().getIdentifier()
方法使用的资源。引用的第三方
AAR
包中如有以上三种情况,也需要解压AAR
,将对应的资源复制一份放到 Portal 工程中。
工程依赖
一个 基于 mPaaS 框架 的 App 包括 一个或多个 Bundle 和 一个 Portal。一个 App 有且只能有一个 Portal 工程,但可以有多个 Bundle 工程。
通过 mPaaS 插件,Portal 工程把所有的 Bundle 工程包合并成一个可运行的 .apk
包。合并后,该插件把 Bundle 工程部署至指定的仓库地址。该仓库地址在 Bundle 的主 module 中的 build.gradle
中定义,如下代码所示:
uploadArchives {
repositories {
mavenLocal()
}
}
该仓库地址是上传至本地的 ~/.m2
仓库地址。您也可以添加自定义的仓库地址,如下所示:
mavenDeployer {
mavenLocal()
repository(url: "${repository_url}") {
authentication(userName: 'userName', password: 'userName_pwd')
}
snapshotRepository(url: "${repository_url}") {
authentication(userName: 'userName', password: 'userName_pwd')
}
}
上传之后,Bundle 以 groupid:artifactid:version:classifier@type
的形式存在指定的仓库中。因此,只要在 Portal 最外层主 module 的 build.gradle
中声明 dependency
就可指定各 Bundle 依赖,如下代码所示:
dependencies {
bundle 'com.alipay.android.phone.mobilesdk:quinox-build:2.2.1.161221190158:nolog@jar'
manifest 'com.alipay.android.phone.mobilesdk:quinox-build:2.2.1.161221190158:AndroidManifest@xml'
}
此外,Bundle 工程之间的相互依赖也需要在 Bundle 的最外层的 build.gradle
中声明仓库依赖地址。
以下配置中的 username
和 password
不是控制台的登录用户名和密码。请搜索群号 41708565 加入钉钉群进行咨询获取这两个值。其中:
mavenLocal()
描述依赖的本地仓库地址。maven{}
声明依赖的远程仓库地址。
allprojects {
repositories {
mavenLocal()
mavenCentral()
maven {
credentials {
username "{username}"
password "{password}"
}
url "http://mvn.cloud.alipay.com/nexus/content/repositories/releases/"
}
}
}
Bundle 编译打包结果
使用 mPaaS 插件编译打包后,一个 Bundle 会生成一个工程包(是一个 .jar
包)。更多信息,请参考 Bundle 工程包 和 Bundle 接口包。
工程包会以 groupid:artifactid:version:classifier@type
的形式发布到指定仓库中。发布仓库地址在 Bundle 主 module 中的 build.gradle
中定义,示例如下:
uploadArchives {
repositories {
mavenLocal()
}
}
上述配置指定发布仓库为本地 Maven 仓库(mavenLocal
)。如需修改本地 Maven 仓库地址(默认 ~/.m2
)或增加发布仓库,请参见 配置发布仓库。
添加 Bundle 依赖
您可以在 Portal 中添加 Bundle 依赖,也可以在 Bundle 中添加其他 Bundle 依赖。只需:
在 Portal 或 Bundle 最外层的
build.gradle
中声明依赖仓库地址。依赖仓库应和上文 Bundle 发布仓库相对应。依赖仓库的配置方法,请参见 配置依赖仓库。在 Portal 或 Bundle 主 module 的
build.gradle
中声明dependencies
依赖。如添加 Bundle(quinox
)依赖的示例如下:
dependencies {
bundle 'com.alipay.android.phone.mobilesdk:quinox-build:2.2.1.161221190158:nolog@jar'
manifest 'com.alipay.android.phone.mobilesdk:quinox-build:2.2.1.161221190158:AndroidManifest@xml'
}