Android Small插件化

一,介绍

Small插件化方案适用于将一个APK拆分为多个公共库插件、业务模块插件的场景。框架对比详见:https://github.com/wequick/Small/blob/master/Android/COMPARISION.md
官网:http://code.wequick.net/Small
GitHub:https://github.com/wequick/Small

二,接入

1.创建工程
打开Android Studio,点选 Start a new Android Studio project 创建一个Android工程。Application Name 本示例设置为 MySmall (您可以填写为自己的项目名称)。
这里写图片描述
设置最小支持 SDK (Small允许支持到 API 9):
这里写图片描述
添加一个 Empty Activity,这个Activity将作为启动画面。
2.创建与配置工程
在 buildscript > dependencies 下添加Small编译插件 gradle-small:

buildscript  {
    dependencies {
        classpath 'net.wequick.tools.build:gradle-small:1.2.0-alpha6'
    }
}

apply plugin: 'net.wequick.small'//引用 gradle-small 插件

small {
    aarVersion = '1.2.0-alpha6'//设置Small运行库版本:
    strictSplitResources=false
    buildToAssets=false
}

aarVersion
Small AAR 库版本。用于指定maven依赖 net.wequick.small:small:$aarVersion,该依赖将被自动添加到宿主即各个插件模块中。
strictSplicResources
是否严格分离资源。默认为 true ,即不允许插件模块携带包含资源的第三方库。
buildToAssets
是否将插件作为 apk文件 打包到宿主apk的 assets 目录下。默认为 false ,即作为 so文件 打包到宿主apk的 lib 目录下。
配套的,你需要在宿主的 Application 里指定是否从 assets 读取插件:

@Override
public void onCreate() {
    super.onCreate();

    // ...

    Small.setLoadFromAssets(BuildConfig.LOAD_FROM_ASSETS);
}

BuildConfig.LOAD_FROM_ASSETS 将会被 Small 自动赋值为你配置的 buildToAssets。
子配置:

small {
    android {

    }
}

用于统一各个模块的Android编译环境配置,包括以下子项:

compileSdkVersion

编译用的 Android SDK 版本。影响到各个子模块的

android {
    compileSdkVersion $compileSdkVersion
}

buildToolsVersion
编译工具版本。影响到各个子模块的

android {
    buildToolsVersion $buildToolsVersion
}

supportVersion

Support包版本(包括 support-v4,appcompat等等)。影响到各个子模块的

dependencies {
    compile 'com.android.support:xx:$supportVersion'
}

bundles

插件类型配置:

bundles 类型, 模块名

bundles 类型, [模块1, 模块2]

允许的类型包括:
host,宿主模块
stub,宿主分身模块
lib,公共库插件模块
app,应用插件模块

配置好以上信息后,同步代码
在底部面板 Terminal 中输入以下命令来验证Small环境:

./gradlew small

如果一切正常,将成功输出:

### Compile-time
  gradle-small plugin : 1.1.0-beta4 (maven)
            small aar : 1.1.0-alpha1 (maven)
          gradle core : 2.14.1
       android plugin : 2.2.3
                   OS : Mac OS X 10.12.1 (x86_64)
### Bundles

| type | name | PP | sdk |  aapt  | support | file | size |
|------|------|----|-----|--------|---------|------|------|
| host | app  |    | 25  | 25.0.2 | 25.1.0  |      |      |

不同版本的输出内容可能会有细微差异
配置宿主
由于加载插件需要对Application注入一些方法,我们对包名目录 app > java > com.example.mysmall 右键 New > Java Class 来新建一个Application,如SmallApp:
这里写图片描述
添加构造方法来初始化Small:

public class SmallApp extends Application {

    public SmallApp() {
        Small.preSetUp(this);
    }

}

由于ContentProvider在onCreate之前被调用,为支持在插件中使用该组件,我们需要提前到构造方法来对之进行懒加载。
如果不需要支持该组件,你也可以放到 onCreate 方法中。
这个方法在应用正常启动时只做一些简单的hook,不会影响性能;但在应用异常启动(后台被杀)时会同步加载插件以保证程序正常运行。

再在 AndroidManifest.xml 中指定这个 Application。

<application
    android:name=".SmallApp"
    ...>
</application>

2.创建插件模块
右键 app 模块 > New > Module:
这里写图片描述
创建一个应用模块 Phone & Tablet Module,设置 Application/Library name 为 App.main,此时 Module name 自动为 app.main,Package name 自动为 com.example.appmain:
这里写图片描述

Module name 以 app.* 命名的模块将被 Small 在 编译时 识别为应用插件模块。 Package name 以 app*
结尾的插件将被 Small 在 运行时 识别为应用插件。
更多有关插件的命名规范与自定义,可以参考插件模块。

为了确认我们确实启动了插件,我们修改插件的布局文件 app.main > res > layout > activity_main.xml,将 TextView 的内容改为 Hello Small!:

<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="Hello Small!" />

3.编译插件
在 Terminal 面板,先编译公共库:

./gradlew buildLib -q

宿主是最基础的一个公共库

再编译 app.main 插件:

 ./gradlew buildBundle -q -Dbundle.arch=x86

为了方便模拟器运行,本示例指定生成插件到 x86 架构下,关于 bundle.arch 选项的细节可以参考编译选项。 如果不想打包成
*.so ,可以参考打包插件为apk。

查看编译情况:

 ./gradlew small

应看到生成的 appmain.so 插件:

这里写图片描述

PP即插件包的资源ID分段,0x77 是 Small 根据模块名哈希自动指派的,你也可以通过配置来自定义资源ID分段。

4.启动插件
现在我们已经生成了插件并内置到宿主包中,要启动插件,我们需要配置一个路由来指向它。
(1)右键 app 模块,New > Folder > Assets Folder 新建 assets 目录:
(2)再右键生成的 assets 目录,New > File 新建路由配置文件 bundle.json:
修改 bundle.json 添加路由:

{
  "version": "1.0.0",//是 bundle.json 文件格式版本,目前始终为 1.0.0
  "bundles": [//插件数组
    {
      "uri": "main",//插件唯一ID
      "pkg": "com.example.appmain"//插件包名
    }
  ]
}

通过这个配置,main 将被路由向 com.example.mysmall.appmain#MainActivity,更多配置的细节可以参考插件路由。

回到宿主的 app > java > com.example.mysmall > MainActivity,在 onStart 方法中我们通过上述配置的 uri 来启动 app.main 插件:

@Override
protected void onStart() {
    super.onStart();
    Small.setUp(this, new Small.OnCompleteListener() {
        @Override
        public void onComplete() {
            Small.openUri("main", MainActivity.this);
        }
    });
}

5.运行宿主
成功运行后,将启动插件模块:
这里写图片描述

三,提取公共库插件

在我们创建了一个应用插件app.main后本身会存在一些资源,这些资源中有一些包括了主题,界面边距等样式,在多个插件中,这些资源应该是可以复用的,这个时候我们就需要通过提取公共插件模块来解决这个问题。
1.创建公共库插件模块
首先创建一个Android Library模块,名字定义为lib.style
将在app.main插件下的公用资源文件拷贝到lib.style插件的资源下
2.添加公共库引用
修改 app.main/build.gradle,增加对 lib.style 的依赖:

dependencies {
    ...
    compile project(':lib.style')
}

3.添加插件路由

{
  "pkg": "com.example.libstyle"
}

4.重新编译插件
清除公共库:

./gradlew cleanLib -q

编译公共库:

./gradlew buildLib -q -Dbundle.arch=x86

编译业务单元:

./gradlew buildBundle -q -Dbundle.arch=x86

5.重新运行后即可看到效果

四,插件模块

插件模块是 Small 特有的插件化与IDE完美结合的产物。做到了“模块即组件,组件即插件”。 组件是工程的角度,插件是应用的角度。只有做到了清晰的组件解耦,才能更好的拆分插件模块。

出于业务需求考虑,Small定义了两类插件:公共库插件与应用插件。 应用插件相对简单,就是用来把大应用拆分成一个个小的业务单元。而公共库插件则是为这些业务单元提供公共的代码与资源。
一,公共库插件模块
公共库插件模块在 开发时 可以通过 compile project(‘:插件模块名’)来被 应用插件模块 所引用。 同时在 编译时 (buildLib) 会被打包成为一个可独立更新的插件。
定义公共库插件模块有两种方式:

1.指定 Module name 为 lib.*
2/在 Small DSL 中显式指明 bundles lib your_module_name

要正确读取到打包的公共库插件也有两种方式:

1.指定 Package name 为 .lib.* 或 .lib*
2.在 bundle.json 中添加描述 “type”: “lib”

二,应用插件模块
应用插件模块在 开发时 可以独立运行。 同时在 编译时 (buildBundle 或 :模块:aR ) 会被打包成一个可独立更新的插件。

定义应用插件模块有两种方式:

1.指定 Module name 为 app.*
2.在 Small DSL 中显式指明 bundles app your_module_name

要正确读取到打包的公共库插件也有两种方式:

1.指定 Package name 为 .app.* 或 .app*
2.在 bundle.json 中添加描述 “type”: “app”

五,插件路由

为了方便插件之间的跨平台调用,Small 提供了 bundle.json 来完成插件路由。
bundle.json 的基本格式为:

{
  "version": "1.0.0",
  "bundles": [
    { bundle1 },
    { bundle2 },
    {   ...   },
    { bundleN }
  ]
}

其中 version 暂为当前文件格式版本,始终为 1.0.0。 bundles 为插件数组。每个插件遵循以下配置。
插件配置
pkg
当前插件的包名,比如 net.wequick.example.small.app.main。
uri
指定当前插件的唯一ID,比如 main。 同时这个 ID 也作为插件的主路由。在Android平台下主路由的表现为 Android:pkg 插件下的 LauncherActivity
type
插件类型:

lib - 公共库插件
app - 业务插件

默认从 pkg 中解析:

当 pkg 包含 .lib. 或以 .libxx 结尾时,取 lib
当 pkg 包含 .app. 或以 .appxx 结尾时,取 app
如果 pkg 不遵循上述格式,需要显式指明。
rules
插件的子路由表。假设在 uri 为 home 的插件下指定:

{
  "uri": "home",
  "rules": {
    "abc": "Some"
  }
}

“home/abc” 将被路由往 home 插件下的 Some 界面,在Android平台下的表现为:pkg.SomeActivity
插件调用

Small.openUri("home", context);

参数传递

// app.detail/MainActivity
Uri uri = Small.getUri(this);
if (uri != null) {
    String id = uri.getQueryParameter("id");
    // Do stuff by `id'
}

六,编译选项

在运行 ./gradlew xx 命令时,可以加入 -Dxx 选项来控制编译方式,包括:

bundle.arch

在 buildToAssets 为 false 的情况下,使用 -Dbundle.arch=xx 可以指定 so 形式插件生成到 [宿主目录]/smallLibs/xx 目录下。

通常在调试模拟器时,你可以使用 x86,在真机打包时使用 armeabi。

bundle.minify

用于指定是否混淆插件,如 ./gradlew buildLib -Dbundle.minify 将对所有公共库插件进行混淆。

org.gradle.parallel

这个是系统自带的选项,用于指定是否并行编译。之所以在这里提及是因为 buildLib 的时候不能打开这个选项,因为公共库插件依赖了宿主,需要宿主编译完成才能正确引用资源。

所以如果你使用持续集成的话,可以用以下的命令集合:

./gradlew cleanLib -q -Dorg.gradle.parallel=true
./gradlew buildLib -q -Dorg.gradle.parallel=false -Dbundle.minify=true
./gradlew cleanBundle -q -Dorg.gradle.parallel=true
./gradlew buildBundle -q -Dorg.gradle.parallel=true -Dbundle.minify=true
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值