开源库小技巧+1,在 ContentProvider 中初始化

3,780 阅读3分钟

情景模拟

最近小明开源了一个 Android 三方库,接入流程很简单,开发者们只需要在应用的 Application 的 onCreate() 方法中去初始化它,然后就可以调用相应的库 API 了——

public class App extends Application {
	@Override
	public void onCreate() {
		super.onCreate();
		XiaomingLibrary.init(this);
	}
}

这是一个很常见的三方库接入后的初始化流程,但是作为库的开发者角度考虑,有没有将库的初始化流程这一步再缩减,让开发者的接入流程更简单呢?如果有的话,像 leakcanary 这种仅需要在 Application 中初始化,又并不需要调用任何 API 的库将会带给开发者一种无任何侵入式的感受。答案当然是有的,作为一个库开发者,你可以在你的库中创建一个 ContentProvider 在 ContentProvider 的 onCreate() 方法中借助 getContext() 方法返回的 Context 来完成你的库初始化,而不需要开发者们去在他们应用的 Application 的 onCreate() 方法中去初始化你的库,当然,这个 getBaseContext() 返回的 Context 的实际类型就是应用的 Application,所以可以借助该 Context 来完成库的初始化。

调用时机

那么 ContentProvider 的 onCreate() 方法是什么时候被调用的呢?它是介于 Application 的 attachBaseContext(Context)onCreate() 之间所调用的,Application 的 attachBaseContext(Context) 方法被调用这就意味着 Application 的 Context 被初始化了,而 ContentProvider 拿到的 Context 也正就是 Application,所以可以在 ContentProvider 的 onCreate() 方法中完成相应的初始化操作 。口说无凭,当然要从代码的角度来证明——读者们应该都知道,在应用的启动过程中,会走到 ActivityThread 的 handleBindApplication() 方法中,在该方法中可以看到 app = makeApplication()installContentProviders(app)mInstrumentation.callApplicationOnCreate(app) 三个方法被相继调用 ——

源码

实际上到这里源码实际上就可以解析完了,从方法名其实就可以看出 makeApplication() 是创建了 Application 并将 Application 实例赋给了 app 这个局部变量,接着 installContentProviders(app) 中会使得 ContentProvider 借助 app 这个局部变量初始化,最后 callApplicationOnCreate(app) 肯定就是调用了这个 app 的 onCreate() 方法。具体流程如下:

makeApplication() 调用链:

LoadedApk#makeApplication() -> Instrumentation#newApplication() -> Instrumentation.newApplication() -> Application#attach() -> Application#attachBaseContext()

installContentProviders() 调用链:

ActivityThread#installContentProviders() -> ActivityThread#installProvider() -> ContentProvider#attachInfo() -> ContentProvider.this.onCreate()

callApplicationOnCreate() 调用链:

Instrumentation#callApplicationOnCreate() -> Application#onCreate()

优缺点

优点很显而易见——免去了使用库的开发者们初始化库的流程,降低了接入成本,这种优势在像 leakcanaryBlockCanary 或者其他一些仅需要初始化而不需要开发者调用任何 API 的库上体现的更加明显,开发者只需要添加依赖就可以使用该库了,完全是0侵入式的接入流程。

缺点:缺点在于它并不一定适用于全部场景,因为 ContentProvider 的 onCreate() 执行在了 Application 的 onCreate() 方法之前,倘若你的库需要有其它业务的依赖(例如你的库需要在其它三方库依赖初始化完成之后才能够初始化)的话,这种方式就并不是很适配你的库。

后记

本文的内容还是十分简单的,但是笔者认为这篇文章带来的不应该仅仅是给读者一种新技能 +1 的感觉,在了解了表面现象之后,更应该去剥开表层探索实现,这样下次在跟小伙伴面前吹的时候,才能够更有底气的展现你的骚操作。最后,为了方便各位读者更好地消化内容,笔者在 github 上上传了例子,戳我直达