開源庫小技巧+1,在 ContentProvider 中初始化

jokermonn發表於2018-08-27

如果你對本文感興趣,也許你對我的公眾號也會有興趣,可掃下方二維碼或搜尋公眾微訊號:mxszgg

開源庫小技巧+1,在 ContentProvider 中初始化

情景模擬

最近小明開源了一個 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 上上傳了例子,戳我直達

相關文章