IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android模块化架构下,子模块自加载方案 -> 正文阅读

[移动开发]Android模块化架构下,子模块自加载方案

Android模块化架构下,子模块自加载方案!

背景

在 Android 模块化架构中后,子Module 间相互解耦,作为独立的模块运行。如果 子Module 也需要进初始化的操作,那么该如何做呢?可能你会说,直接在 壳App Application的onCreate函数进行初始化就可以了,但这样会带来一些新的问题:

  • 我们并不需要 壳App 去关注模块内部的业务,所以每个模块的初始化应该由自身管理;
  • 并不是所有子模块的初始化,都需要在 Application onCreate() 时去进行加载,这样会极大影响应用的启动速度。所以每个模块的初始化应该按需加载;

常见方案及优缺点

1、ContentProvider

实现原理:每个 子Module 内部自定义 ContentProvider ,在应用 Application 的 onCreate 函数执行前,系统就会自动的顺序调用 子Module 的 ContentProvider 的 onCreate 函数,也就实现了 子Module 的自加载功能。例如 WorkManager 也是根据这个原理,其内部声明了 ContentProvider 来实现这种自加载方案。

img

优点:

  • 模块间充分解耦,代码边界独立

弊端:

  • ContentProvider 的启动是有性能损耗的,在子模块较多(业务逻辑较多的 App,可能会有几十个 子Module )的情况下,有一定的性能损耗;
  • 需要继承 ContentProvider 类并在 AndroidManifes 中声明,代码不够简洁和优雅;
  • 无法控制初始化的时机,在应用启动时就会初始化所有的子模块,而初始化本身可能就是一件比较重的业务逻辑;

2、ARouter

实现原理:通过路由的方式,实现子Module的初始化。

img

定义通用IPreloadProvider接口

interface IPreloadProvider: IProvider {
    fun preload()
}

在主module中进行初始化调用

fun ARouter.preload(context: Context, path: String) {
    val preloadProvider = this.build(path).navigation(context.applicationContext) as IPreloadProvider
    preloadProvider.preload()
}

优点:

  • 在多数场景下,我们都会使用 ARouter 来进行模块化设计,而ARouter天然支持这种路由调用的方法,简单好用;
  • 代码边界独立,壳App 与 子Modoule 间不需要代码上的直接依赖;

弊端:

  • 壳App需要关心 子Module 的业务,设计上有一定的耦合;
  • 初始化的代码块容易膨胀,且各个 子Module 的初始化代码均在一起,后续难以阅读和维护;

3、App Startup

img

优点:

  • App Startup 是 为了解决因 App 启动时运行多个 ContentProvider 会增加 App 的启动时间的问题,使用了一个 InitializationProvider 管理多个依赖项,消除了每个库单独使用 ContentProvider 成本,减少初始化时间;
  • App Startup 可以自动初始化 AndroidManifest.xml 文件中 InitializationProvider 下面的 声明要初始化的模块,并允许自定义模块初始化顺序;
  • App Startup 提供了一种延迟初始化模块的方法,减少 App 初始化时间;

弊端:

  • 使用时需要修改 AndroidManifest 文件,使用不够简洁;
  • 子Module 功能比较独立,且很多情况不需要即时加载(不需要在应用启动就进行初始化的动作),如果使用延迟加载的话,又需要使用代码主动执行初始化操作,那么势就必存在对应的引用。而我们的 子Module 依赖其实是建议使用 runtimeonly ,减少代码上的直接依赖;
  • 无法解决需要时加载这种需求;
  • 加载时序是在 Application onCreate 之前,依赖的通用库此时可能并未被加载成功;

4、方案总结

所以,我们需要一个模块自加载方案,能够解决上面的问题,且能满足各种场景的需求:

  • 代码简洁,使用方便,逻辑清晰;
  • 子Module 的预加载或者预启动,由 子Module 自身来管理,避免与其他模块的逻辑耦合;
  • 加载时机可以控制,能做到即时加载或者懒加载,避免产生性能损耗;
  • 不产生额外的性能损耗(可接受范围内);

模块化自加载方案(ALoader)

方案: 每个 子Module 都有自己的 ModuleApplication(虚拟Applicaiton),并定义启动模式,在应用 Application 启动时,会根据每个 ModuleApplication 的启动模式来择机调用其 onCreate() 函数,达到模块自加载的目的。

使用非常方便,仅需要在每个 子Module 内创建 ModuleApplicaiton 类,继承自IModuleApplication,使用注解:ModuleApplication 定义 initMode 即可。

@ModuleApplication(initMode = InitMode.MAIN)
class LoginModuleApplication : IModuleApplication() {

    override fun onCreate(context: Context) {
        Log.v("ALoader", "Login Module onCreate")
    }
}

InitMode:

  • HUNGRY:饿汉式加载

主要用于加载时机较为严格的场景,即应用启动后,需要立刻加载该 子Module 的相关配置。

  • IDLE:空闲时加载

主要用于加载时机不那么严格的场景。即应用启动后,使用 idleHandler 进行初始化,达到空闲时加载的目的。

  • LAZY:懒汉式加载

主要用于按需加载的场景。即应用启动后,不进行该 子Module 的初始化操作,待该 子Module 的 Activity 、 Service 或者相关逻辑执行时,才进行 该子Module 的初始化工作,保证功能的正常运行。

自加载方案设计思路

借鉴了ARouter的实现原理,模块自加载方案的核心思想是:通过Apt技术,生成 InitMode(初始化时机) 和 被注解(@ModuleApplication)的组件类 的映射关系的类(ALoaderCore),利用这个保存了映射关系的类,ALoader根据 InitMode 寻找到目标类,执行其onCreate()函数。该框架的核心是利用 apt 生成的映射关系。

img

1、如何提高ALoader的初始化速度

利用缓存和 Gradle 插件,来提升映射类的查找速度,实现高性能的初始化。

img

2、如何实现ALoader懒加载的效果

对于 子Module 懒加载的实现,最重要的是如何识别到该 子Module 开始启动,并在此之前进行初始化的操作。针对于多数的业务场景来讲,子Module 的启动通常是伴随着 四大组件(Activity、Service、ContentProvider、BroadcastReceiver)的启动,而我们可以通过 Hook 技术来实现对四大组件启动的感知,通过包名的匹配来识别到是哪个 子Module 开始启动。以 Activity 和 Service 为例:

fun hookActivityThreadHHandler() {
        val aClass = Class.forName("android.app.ActivityThread")
        val sCurrentActivityThread: Field = aClass.getDeclaredField("sCurrentActivityThread")
        sCurrentActivityThread.isAccessible = true
        val activityThread = sCurrentActivityThread.get(aClass)

        val mHField: Field = aClass.getDeclaredField("mH")
        mHField.isAccessible = true
        val mh = mHField.get(activityThread) as Handler

        val handlerClass = Class.forName("android.os.Handler")
        val mCallbackField = handlerClass.getDeclaredField("mCallback")
        mCallbackField.isAccessible = true
        mCallbackField.set(mh, ProxyHandlerCallback())
    }

    class ProxyHandlerCallback : Handler.Callback {

        override fun handleMessage(msg: Message): Boolean {
            when (msg.what) {
                LAUNCH_ACTIVITY -> {
                    // TODO: 
                }
                EXECUTE_TRANSACTION -> {
                    // TODO: 
                }
                CREATE_SERVICE -> {
                    // TODO: 
                }
            }
            return false
        }
    }

方案不足之处和优化点

该方案的最终目的是:通过最简单的方式,实现模块的按需自加载方案。

但是依然还有可以优化的地方:

  • App Startup 可以自定义模块初始化顺序,在ALoader方案中,是否也需要集成?
  • 通过 Hook 技术来判断子模块的启动,可能存在兼容性问题,是否还有更优雅的方式?
    e
    }
    }

## 方案不足之处和优化点

该方案的最终目的是:**通过最简单的方式,实现模块的按需自加载方案。**

但是依然还有可以优化的地方:

- App Startup 可以自定义模块初始化顺序,在ALoader方案中,是否也需要集成?
- 通过 Hook 技术来判断子模块的启动,可能存在兼容性问题,是否还有更优雅的方式?
- 是否有其他更复杂的业务场景,还没考虑到呢?
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-12-16 17:47:32  更:2021-12-16 17:48:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 8:42:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码