使用Small进行Android模块化开发

作者: 黄怡菲 | 来源:发表于2016-11-18 16:21 被阅读4791次

如果你还未尝试过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上看到

工程目录结构

small_framework.png

上图是一个建议的工程结构图,一个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是安静模式,可以让输出更好看,也可以不加)

small_build_lib.gif

可以看到buildLib不仅编译了lib.xxx,还编译了宿主模块。

编译业务插件

[./]gradlew buildBundle -q

small_build_bundle.gif

单独编译一个插件

[./]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的运行可以分为三步

  1. 框架的初始化
  2. 插件的初始化
  3. 通过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热更新源码

  1. 通过接口获取更新信息,里面可以包括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**使更新后的插件生效

相关文章

网友评论

  • 青蛙要fly:small不是插件化开发么。怎么标题说是模块化开发了。
  • 07a60879555c:gradlew buildLib -q时出错,
    E:\android\Yomo>gradlew buildLib -q

    FAILURE: Build failed with an exception.

    * Where:
    Build file 'E:\android\Yomo\build.gradle' line: 4

    * What went wrong:
    A problem occurred evaluating root project 'Yomo'.
    > com/android/build/gradle/api/BaseVariant : Unsupported major.minor version 52.0

    * Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
  • 草蜢的逆袭:文章很不错,可以分享一下源码吗?xfgczzg@163.com,谢谢
    leef_7ac2:很不错呀
    雨颂黄昏花易落:@syusikoku下载之后编译一下就可以了
    黄怡菲:@syusikoku https://github.com/wequick/Small/tree/master/Android 这个是官网的例子,sample目录下的应该能直接运行。

本文标题:使用Small进行Android模块化开发

本文链接:https://www.haomeiwen.com/subject/obqspttx.html