bundles-assembler

150 阅读6分钟

小手动一动,点赞转发加关注。微信搜索【大前端杂货铺】公众号关注大前端老司机带您遨游大前端知识的海洋。关注 Github github.com/big-fronten… 还有大前端代码实践哦。

首页

bundles assembler 是一款编排bundle以此获取 构建速度/启动速度/内存占用/包体积 最大收益的项目

先定义一下bundle和foundation,bundle是依附于app framework的native bundle、flutter bundle、react native bundle、hybrid bundle,有些bundle具有动态性能被app framework动态加载;foundation是赋予上层能力的基础服务,更像是一些用来快速开发页面的toolkits,比如网络、存储、图像、音视频都是foundation。bundle之间相互解耦,bundle之间的通信主要有两种页面路由和rpc。

architecture

能力

声明了一些名词解释,我们来看看bundles assembler能提供怎么样的能力:

  • 🚀 bundle多种加载方式:根据业务模块的重要性,可选择instant 、delay 、lazy 三种加载方式来优化构建速度/启动速度/内存占用等指标
  • 📱 业务模块通讯IBC:自研IBC代码量少且容易使用
  • ⌨️ 模块选择器:高效便捷的idea交互插件,让你像罗老师一样管理自己的各个小模块
  • 免费maven仓库:提供免费的JamesfChen Snapshots(gradle.property文件 useJamesfChenSnapshots=true),让你免去费时费力搭建组件maven
  • 📚高效工程化脚本:使用命令行轻松完成模板创建,项目脚手架初始化等工作

技术方案对比

框架JIMUbundles-assembler
工程脚手架×
模块选择idea插件×
模块加载支持立即加载支持立即/延迟/懒加载
模块发布支持静态模块支持静态模块/动态模块
支持多种路由库
app集成方式jar/aar/source静态集成jar/aar/source/apk 动静结合集成

原理

对于bundle的加载方式有如下三种:

  • bundle立即加载(instant load)
  • bundle延迟加载(delay load)
  • bundle懒加载(lazy load)

回顾一下插件化与组件化

解耦的方式构建/执行打包方式
组件化编译时aar/jar
插件化运行时apk/dex

先来说说立即加载,通过dexbuilder在编译期间将aar/jar的dex整合到app的dex中,然后经过art加载执行。 再来说说延迟加载 与 懒加载 ,两者的区别在于,前者会在MQ处于idle或者 draw end的阶段进行加载,不与界面的初始绘制抢夺cpu,后者使用时才会进行加载(在IBC接口调用时,才进行模块加载)。两者的共同点都是利用了插件化的技术实现,经由apkbuilder打包为apk,在安装加载时,会被extra出dex被ClassLoader加载 或者 直接被LoaderApk加载。

项目地址:bundles-assembler

快速入门

项目初始化

pip3 install bundcli

命令列表

通过bundcli init初始化项目模板工程

[1] % bundcli init --help
usage: command line init [-h] [-p PACKAGE] [-n NAME]

optional arguments:
  -h, --help            show this help message and exit
  -p PACKAGE, --package PACKAGE 项目包名
  -n NAME, --name NAME  项目名字

bundles-assembler-plugin

//app 模块
plugins{
      id 'io.github.jamesfchen.app-plugin'
}
//foundation插件用于基础功能模块
plugins{
    id 'io.github.jamesfchen.foundation-plugin'
}

创建模块

静态bundle

1.接入

bundcli可以创建bundle模板(native_static)

[0] % bundcli create --help
usage: command line create [-h] [-p PACKAGE] [-n PATH] [--type {none,flutter,reactnative,html5,native_stactic,native_dynimac}]

optional arguments:
  -h, --help            show this help message and exit
  -p PACKAGE, --package PACKAGE 模块包名
  -n PATH, --name PATH  模块名字
  --type {none,flutter,reactnative,html5,native_stactic,native_dynimac}  模块类型

手动接入

module_config.json

    {
      "simpleName": "home-myhome",
      "sourcePath": ":home-myhome",
      "format": "nsbundle",
      "group": "home"
    }

bundles-assembler-plugin

//bundle插件自带路由,该插件主要用于bundle模块
plugins{
    id 'io.github.jamesfchen.nsbundle-plugin'
}
//业务组件ibc中的cpbc 模块
plugins{
      id 'io.github.jamesfchen.api-plugin'
}

2.静态bundle选择工具

集成app时,组件有三种状态

  • source:源码形式集成
  • binary:aar/jar包集成
  • exclude:不集成 bundles

3.IBC

组件通信(IBC,inter-bundle communication)在解耦的模块中是一把利器,主要分为页面路由(Router)与接口调用(CBPC,cross bundle procedure call)

首先需要在app项目的build.gradle引入app-plugin插件

plugins{
      id 'io.github.jamesfchen.app-plugin'
 }

然后在bundle项目的build.gradle引入bundle-plugin插件

plugins{
      id 'io.github.jamesfchen.nsbundle-plugin'
 }

3.1.Router

  • 利用android framework层的intent uri路由跳转
  • 在app framework实现路由跳转,需要将app层的路由器发布到app framework的路由器管理中心,当需要跳转时,app framework会到管理中心find获取路由器,然后进行跳转

页面路由的实现有上面两种,任何一种都可以做到页面的跳转,两种方案各有优缺点。

首先提供路由页面的路由器,在编译的时候会自动将扫到的所有路由器注册到路由表。

@Router(bindingBundle = "bundle1")
class Bundle1Router : IRouter {
    override fun onOpen(cxt: Context, page: String, params: Bundle?): Boolean {
        if ("sayme".equals(page, ignoreCase = true)) {
            val intent = Intent(cxt, SayMeActivity::class.java)
            cxt.startActivity(intent)
            return true
        }
        return false
    }
}

接下来提供java和kotlin的使用

java

                UriBuilder uriBuilder =new  UriBuilder();
                uriBuilder.setUri("b://bundle2/sayhi");
                IBCRouter.open(SayMeActivity.this,uriBuilder);

kotlin

          binding.btNative1.setOnClickListener {
//            打开当前bundle内部的页面
            IBCRouter.open(this) {
                uri = "/ppp"
            }
        }
        binding.btNative2.setOnClickListener {
            //打开当前bundle内部的页面
            IBCRouter.open(this) {
                uri = "b://bundle1/sayme"
            }
        }
        binding.btReact.setOnClickListener {
            //必须自定义router且bindingBundle=h5container
            IBCRouter.open(this) {
                uri = "https://spacecraft-plan.github.io/SpacecraftReact/#/"
                params(
                    "key2" to "cjf2",
                    "key3" to 1,
                    "key4" to true
                )
            }
        }
        binding.btH5.setOnClickListener {
            IBCRouter.open(this) {
                uri = "b://h5container/page"
                params(
                    "url" to "file:///android_asset/AApp.html",
                )
            }
        }
        binding.btRn.setOnClickListener {
            IBCRouter.open(this) {
                uri = "b://h5container/page"
                params(
                    "url" to "file:///android_asset/AApp.html",
                )
            }
        }

3.2.CBPC

  • 暴露api给外部bundle模块,然后内部实现接口,需要在app framework注册暴露的api,方便search,实现方式与页面路由的第二种方法相似

在export模块的build.gradle需要引入api-plugin插件

plugins{
    id "io.github.jamesfchen.api-plugin"
}

同时要声明要暴露的接口

abstract class ICall : IExport() {
    abstract fun call():Boolean
}

callee模块

@Api
public class CallImp extends ICall{

    @Override
    public boolean call() {
        Log.d("cjf","onCall");
        return true;
    }
}

caller模块

            val api = IBCCbpc.findApi(ICall::class.java)
            if (api.call()) {
               ...
            }

callee模块与caller模块都要依赖export模块,且两者不能相互依赖。

动态bundle

插件包可以减少包体积也可以按需加载模块,功能多多

1.接入

cli命令行接入

bundcli可以创建动态bundle模板,该模板的初次加载方式有远程服务器加载与内置加载两种方式。

[0] % bundcli create --help
usage: command line create [-h] [-p PACKAGE] [-n PATH] [--type {none,flutter,reactnative,html5,native_stactic,native_dynimac}]

optional arguments:
  -h, --help            show this help message and exit
  -p PACKAGE, --package PACKAGE 模块包名
  -n PATH, --name PATH  模块名字
  --type {none,flutter,reactnative,html5,native_stactic,native_dynimac}  模块类型

手动接入

module_config.json

    {
      "simpleName": "plugin-im",
      "sourcePath": ":plugin-im",
      "versionCode":"3",
      "versionName":"3.0.0-SNAPSHOT",
      "format": "ndbundle",
      "group": "im",
      "dynamic": "local-plugin"
    },

bundles-assembler-plugin

plugins{
    id 'io.github.jamesfchen.ndbundle-plugin'
}

2.动态bundle选择工具

  • source:源码形式集成到apk
  • binary:插件包以apk-zip/jsbundle包放置在assets或者远程服务器
  • exclude:不集成 bundles

3.IBC

   IBCRouter.open(this) {
       uri = "b://im/nav"
   }

通过IBC api就能进行通信,同静态bundle IBC使用方式一样

framework foundation模块

framework中的模块是为上层的业务模块提供基础能力,在项目中必须参与编译

1.接入

cli命令行接入

...

手动接入

    {
      "simpleName": "framework-network",
      "sourcePath": ":framework:network",
      "format": "foundation",
      "group": "fwk"
    },

bundles-assembler-plugin

plugins{
    id 'io.github.jamesfchen.foundatioin-plugin'
}

2. foundation选择工具

  • source:源码形式集成到apk
  • binary:aar形式 bundles

小手动一动,点赞转发加关注。微信搜索【大前端杂货铺】公众号关注大前端老司机带您遨游大前端知识的海洋。关注 Github github.com/big-fronten… 还有大前端代码实践哦。