怎样初始化才好

问题

每个 App 都会在启动时初始化一坨东西,这个点有两个问题:

  • 代码的依赖关系应该是有向无环图,但是初始化是线性的,所以相当于手动进行了一次拓扑排序,而且这个排序是写死的。这里把耦合以一种文档的形式写到了一起,一方面引发了维护文档的问题;另一方面导致所有人需要同时修改一个文件
  • 某些初始化本身是一个耗时的操作,启动初始化会严重拖慢 App 启动

想法

解决这些问题的核心点就是懒加载。让所有功能在真正被调用的点再初始化,就像构造函数应该在对象构建时再调一样。
如果把功能看做一个类,初始化完全就是构造函数。为啥不能直接用构造函数呢?因为通常在合理的分层下,需要初始化的功能在底层,并不能依赖到运行环境中的 Context 或者具体到使用者的配置,所以,需要在构造之外再提供一个初始化接口。而构造只是起到内存分配的作用。

方案

方案的前提是当前 App 有一个通用可控的 ioc 框架。
首先要提供一个提供初始化能力。保证:

  • 可重入
  • 线程安全
  • 保证调用时已初始化完毕

按最常见的 sdk + 接入方 + 使用方的形式。由 sdk 提供能力,接入方在 sdk 上附加一个初始化(接入)功能,最后在使用方使用,无需关心初始化。有两种 case:

  • 对于可控制构建的代码,提供一个构建时的切面,在这个切面进行初始化。这里分为两种:
    • 基于 ioc 框架的单例/plugin。直接在 ioc 要求 sdk 和使用方都收拢到 ioc 的使用方式上。
    • 基于 ioc-factory 的二次封装。面向 sdk 不使用 ioc,sdk 内部不会主动调用单例的情况(初始化不会由 sdk 触发)。
  • 对于不可控构建的代码,提供调用点的切面,在这个切面进行初始化。场景同样分为两种:
    • 完全三方库,例如 Fresco。由于接口设计不受控,所以只能针对其接口设计和使用方式,接入方按实现进行手动切面。必然涉及到的是 AspectJ
    • 二方库。接口设计受控,首先应该确定是否能依赖 ioc 框架,将问题转化成基于 ioc 的初始化方案。如果不宜依赖 ioc,则保证所有依赖初始化的调用收拢至同一个简单类,再针对这个简单类进行切面

上面就是一个完备的初始化方案,按 sdk + 接入方 + 使用方来说。初始化由接入方完成,以切面的形式插入到整个使用过程中,sdk 和使用方都对此无感知。

优势

使用这个思路,首先是使用懒加载保证所有初始化在且仅在第一次使用时初始化,避免了维护一个文档、以及初始化不及时的问题;其次,由于达成了全功能的懒加载,启动速度可以达到当前业务逻辑下的上限,如果有提速要求,基本只能靠优化业务逻辑了。

你可能感兴趣的:(搞架构,架构呓语)