美文网首页Android知识
小(Small)插件化框架那些事

小(Small)插件化框架那些事

作者: 乐之飞于 | 来源:发表于2017-02-28 17:26 被阅读142次

    需要研究技术:类动态加载,资源动态加载,组件动态注册

    如果需要更新插件,或者新增老插件的协议,还需要更新宿主插件声明信息(bundle.json)。

    要做到以上几点,需要处理文章开头说得三个技术问题

    动态加载类

    这部分主要是通过向BaseDexClassLoader中的DexPathList添加插件类实现的。有很多文章解释这部分的原理,这里就不赘述了。

    前面提到的插件so包可以像一个apk文件被解压,但不能独立运行。Small就是从这些插件so文件中加载Element,并添加到DexPathList中的。

    动态加载资源

    Android是通过AssetManager来加载资源的,默认情况下只会添加

    /framework/base.apk" - Android基本资源

    /data/app/*.apk" - 应用程序资源

    编译过程中aapt会为资源分配id,并存储为PPTTNNNN的16进制整数

    PP代表包信息

    TT代表资源类型

    NNNN定位具体的资源

    Small是通过修改PP字段来避免模块间的资源冲突的。在后面Small的编译过程中会介绍这部分的逻辑。

    动态注册组件

    Activty受Instrumentation监控,都需要通过Instrumentation#execStartActivity来启动并激活声明周期

    而Activity的实例则是在ActivityThread中,通过Instrumentation#newActivty来构建的。

    因此要动态注册Activity,需要在宿主的Manifest中注册一系列的假Activity,来获取Activity的声明周期。

    再通过反射的方式修改系统的Instrumentation,在系统启动假Activity之前,将Activity信息替换为真正的Activity。这部分会在Small运行阶段中详细介绍。

    Small项目的编译过程

    了解完Small的基本原理后,就该Gradle出场了。因为没有插件so包,Small是没法运行的。

    下面介绍Small插件的几个方面:

    Gradle插件的初始化过程

    如何为每个模块分配资源ID

    打出的so包到底是什么

    我们这里只介绍与此相关的Gradle知识,如需补充更多Gradle基础知识可参考最后的几篇文章。对Small源码的分析也仅限于此。

    Gradle插件的初始化过程

    Small的Smaple目录下的build.gradle中有apply plugin: 'net.wequick.small'和classpath 'net.wequick.tools.build:gradle-small:1.1.0-beta3'。前者是指定编译插件,后者是编译插件在maven仓库中的位置。

    而在DevSmaple目录中,我们没有指定classpath也能使用这个插件。这是因为DevSmaple下有一个buildSrc工程,Sample中使用的脚本正是来自buildSrc。

    buildSrc下是一个默认的Groovy工程,专门用于存放相对比较复杂的Groovy脚本,避免build.gradle过大里面主要包括一些properties和Groovy源文件。

    net.wequick.small.properties中指定apply plugin: 'net.wequick.small'时需要运行的Groovy类implementation-class=net.wequick.gradle.RootPlugin,apply是所有Plugin被调用的入口。

    这里可以看到RootPlugin主要做了3件事:创建Extension,配置Project,创建Task。

    创建Task

    这里我们定义了之前用到的buildLib和buildTask任务,我们才能在Android Studio中看到这些任务。当然Small中用到的任务远远不止这四个,而且他们之间有很强的依赖关系。

    small_tasks.png

    创建Extention

    Groovy中定义的Extention就是在Gradle中可以直接配置的一些扩展信息。Android插件的Extention中一定包括compileSdkVersion和buildToolsVersion两个字段,我们才能在Gradle中使用

    android {    compileSdkVersion22buildToolsVersion"22.0.1"}

    Small中的Gradle配置和Groovy的RootExtention也是对应的

    配置Project

    这里只截取了configureProject,这里可以看到,根据项目子模块的类型,还会继续加载对应的Plugin。

    如何为每个模块分配资源ID

    修改PP字段就是在AppPlugin中实现的,LibraryPlugin也是继承AppPlugin,所以这个策略对公共库插件生效。

    可以看到通过一个sPackageIds保存每个插件对应的PP值。

    打出的so包到底是什么

    这里主要关注LibraryPlugin <- AppPlugin <- BundlePlugin,三者是继承关系。

    LibraryPlugin:会将库工程转换为普通工程,从而能生成apk包。除此之外,这里还将生成的资源id都保存在一个public.txt文件中

    AppPlugin:分配PP字段,合并Manifest,合并R文件

    将apk文件命名为so,并放到正确的位置

    Small项目的运行

    Small.preSetUp

    这里主要是Small框架的初始化,主要注册了三种Launcher

    这里主要关注ApkBundleLauncher#onCreate的一小部分,这里通过反射完成两件事

    获取ActivityThread#mInstrumentation,并修改为ApkBundleLauncher#InstrumentationWrapper:这是为了在启动Activity时,将Activity改为Manifest中声明的假Activity,从而能通过Instrumentation的检查。

    获取ActivityThread#mCallback,并修改为ApkBundleLauncher#ActivityThreadHandlerCallback:这是在ActivityThread创建Activity对象前,将ActivityInfo改为真正的Activity。

    Small.setUp和Small.openUri

    Small.setUp是根据bundle.json去加载插件模块。插件的实际加载过程,在ApkBundleLauncher中完成。

    解析apk文件

    加载apk中的资源和类

    调用模块Application#onCreate

    除此之外,Small.setUp还做了更新检测,这里不再赘述。

    Small.openUri更简单,主要是根据uri去匹配对应的Activity

    相关文章

      网友评论

        本文标题:小(Small)插件化框架那些事

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