如果你还未尝试过Small,建议你可以先去github上看看怎么使用Small 。
由于github上的文档较少,而且项目还在发展阶段,所以关于怎么使用Small的线索只能从Issue中寻找线索,而且有些问题是历史版本的问题。
这篇文章主要讲几个实践中的经验,也欢迎大家反馈使用中遇到的问题,一起踩坑。
根目录build.gradle
使用Small架构的工程中,根目录的build.gradle中一定包含
buildscript {
dependencies {
classpath 'net.wequick.tools.build:gradle-small:1.1.0-beta3'
}
}
apply plugin: 'net.wequick.small'
small {
aarVersion = '1.1.0-beta9'
}
- classpath是指定编译时类的依赖关系
- aarVersion是指定运行时使用的small依赖包版本号
- 对于相同的版本号alpha > beta
版本号的历史记录可以在Bintray上看到
工程目录结构
上图是一个建议的工程结构图,一个Android Studio工程中包含三大部分
- 宿主:工程中的app模块,不能依赖lib.xxx
- 公共库插件:工程中的lib.xxx模块,是标准的Android库工程
- 第三方依赖合并到lib.vendor中管理
- 样式相关的资源放在lib.style中管理
- 其他工具类,以及网络、存储等跨业务模块的底层代码可以在lib.utils中管理
- 以上方案只是一个建议,还是要根据项目的特点划分lib.xxx
- 业务插件:工程中的app.xxx模块,是标准的Android应用工程
- app.main一般是程序的入口,并控制业务逻辑的主线
- 其他app.xxx一般是业务逻辑的支线,插件内的业务逻辑之间关联性较强
- app.xxx可以依赖多个lib.xxx
{
"version": "1.0.0",
"bundles": [
{
"uri": "main",
"pkg": "com.example.mysmall.app.main",
"rules":{
"community":".CommunityFragment"
}
},
{
"uri": "lib.main",
"pkg": "com.example.mysmall.lib.main"
}
]
}
在宿主模块的assets目录下的bundle.json声明宿主使用的插件,每个bundle还能定义一些rule去启动特定的Activity。
这里注意,lib模块也要在bundle中定义,否则运行时会出错。
还需要在宿主模块中增加签名配置和共享的依赖库
signingConfigs {
release {
storeFile file('../sign/release.jks')
storePassword "5mall@ndro!d"
keyAlias "small"
keyPassword "5mall@ndro!d"
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
[...]
compile 'com.android.support:design:23.1.1'
关于怎么添加一个插件模块,请参考怎么使用Small
编译
编译公共库插件
[./]gradlew buildLib -q (-q是安静模式,可以让输出更好看,也可以不加)
可以看到buildLib不仅编译了lib.xxx,还编译了宿主模块。
编译业务插件
[./]gradlew buildBundle -q
单独编译一个插件
[./]gradlew -p app.main assembleRelease
对插件的编译,会在app/smallLibs/生成对应的so文件,这些so文件本质上就是独立的apk包。
所以修改某个模块之后,要在运行时生效,必须先编译对应的插件,更新smallLibs下的so文件。
在lib.xxx中删除资源
编译后的lib,如果删除资源再编译就会出现错误
No support deleting resources on lib.* now!
这是为了保证lib的资源id不变,从而可以独立更新而不影响任何依赖它的app插件。具体可参考为什么要使用last built id
所以在删除lib中的资源后,需要删除lib.xxx模块下的public.txt再通过cleanLib, buildLib, cleanBundle, buildBundle
重新编译。
运行过程分析
Small的运行可以分为三步
- 框架的初始化
- 插件的初始化
- 通过uri启动插件
public class SmallApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
CrashHandler.getInstance().setCustomCrashHanler(getApplicationContext());
/**
* 1. Small框架初始化
*/
Small.preSetUp(this);
}
}
public class SplashActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_splash);
/**
* 2. Small插件初始化
*/
Small.setUp(SplashActivity.this, new Small.OnCompleteListener() {
@Override
public void onComplete() {
/**
* 3. 通过uri启动插件
*/
Small.openUri("main", SplashActivity.this)
finish();
}
});
}
}
从插件初始化,到初始化完成会耗费几百毫秒,甚至更多(应该和机器速度和插件规模相关),所以可以考虑将欢迎页放入宿主工程之中。
如果欢迎页有比较复杂的广告逻辑或统计相关的逻辑,则可以考虑在主插件中做一个透明的Activity来处理。
Manifest相关
-
application:可以在插件的Manifest中指定application,每个插件的Application都会在
Small.setup
时被创建 - android:configChanges:由于android:configChanges属性暂未支持,因此对于需要处理android:configChanges的Activity需要在宿主中注册(由于宿主工程和插件工程在编译时是互相隔离的,所以这些定义在宿主中的插件activty需要指定style)
- activity:如果要通过uri启动Activity,则对应Activity应该在app.xxx插件的Manifest中进行注册。否则在通过uri启动时会出错。如果需app插件可以独立运行,但在Manifest中至少要注册一个主Activity。
热更新
- 插件so默认会内置在apk中,如果不内置也可以在启动页下载插件
- 热更新就是更新bundle.json和插件so的过程,为了避免热更新的异常,可以下载bundle先保存到一个临时目录,全下载完之后,统一复制到patch目录下,并标记升级。
具体可参考Small热更新源码
-
通过接口获取更新信息,里面可以包括version,bundles,updates
{
"manifest": {
"version": "1.0.0",
"bundles": [
{
"uri": "lib.utils",
"pkg": "net.wequick.example.small.lib.utils"
},
{
"uri": "lib.style",
"pkg": "com.example.mysmall.lib.style"
},
{
"uri": "lib.analytics",
"pkg": "net.wequick.example.lib.analytics"
},
{
"uri": "main",
"pkg": "net.wequick.example.small.app.main"
},
{
"uri": "home",
"pkg": "net.wequick.example.small.app.home"
},
{
"uri": "mine",
"pkg": "net.wequick.example.small.app.mine"
},
{
"uri": "detail",
"pkg": "net.wequick.example.small.app.detail",
"rules": {
"sub": "Sub"
}
},
{
"uri": "about",
"pkg": "net.wequick.example.small.app.about"
}
]
},
"updates": [
{
"pkg": "net.wequick.example.small.app.about",
"url": "http://wequick.github.io/small/upgrade/libnet_wequick_example_small_app_about.so"
},
{
"pkg": "net.wequick.example.small.lib.utils",
"url": "http://wequick.github.io/small/upgrade/libnet_wequick_example_small_lib_utils.so"
},
{
"pkg": "com.example.mysmall.lib.style",
"url": "http://wequick.github.io/small/upgrade/libcom_example_mysmall_lib_style.so"
},
{
"pkg": "net.wequick.example.small.app.home",
"url": "http://wequick.github.io/small/upgrade/libnet_wequick_example_small_app_home.so"
}
]
}
2. 通过**Small.updateManifest**更新bundles.json和version信息
3. 根据updates下载各插件的so文件,并将so文件复制到对应的补丁文件路径下(通过**Bundle.getPatchFile**获取补丁文件目录)
4. 调用**Bundle.upgrade**使更新后的插件生效