Android 11源碼分析:ContentProvider初始化流程分析(子模塊使用ContentProvider初始化)

ZhaoYun 2021-08-15 18:16:56 阅读数:832

本文一共[544]字,预计阅读时长:1分钟~
android 分析 contentprovider 初始化 初始

有一說一ContentProvider這個東西我平時用的是真的不多,但是這玩意畢竟是安卓四大組件之一,他的作用不可忽視。怎麼使用他是我們作為安卓開發者必須掌握的技能之一。本文將在源碼的角度上,分析ContentProvider初始化流程.

關於ContentProvider的題外話

ContentProvider作為四大組件之一,作用是IPC(跨進程通信),底層實現是Binder,經過系統的封裝後,ContentProvider的使用方式已經很簡單了我就不囉嗦了。實不相瞞,我頭一次使用ContentProvider並非是拿來做進程通信,而是在一次項目架構昇級的時候,作為子模塊初始化使用。

當項目迭代到一定程度後,項目的編譯會占用我們開發的很多時間,剛入坑安卓開發的時候我也不太理解為什麼很多博主强調加編譯速度,直到我們的項目,一次運行編譯需要20分鐘以上的時候,才意識到需要做編譯優化了,編譯優化有很多點,這裏不一一描述,這裏只說一點:使用ContentProvider代替Application做初始化

項目之前對於一些常用的模塊,都作為app的子模塊依賴,子模塊有自己的Application,使用Appjoint的方式,在子模塊的Application上加注解,使用方式如下:

@ModuleSpec(priority = 10)
class UmengApplication : Application() {
}
複制代碼

但是後面我為了加快編譯速度,决定將一些相對固定的子模塊打成aar的方式依賴,比如底層網絡模塊,數據庫模塊,友盟等等。因為前期開發這些模塊是需要不停修改的,慢慢的這些模塊趨於穩定了,就可以打成aar了,然後在在gradle做判斷,如果某個開發需要修改則進行源碼依賴,否則使用aar依賴。將子模塊打成aar後,我發現子模塊的Application不會初始化了,因為AppJoint不支持。所以想到了使用ContentProvider來進行子模塊的初始化操作。

正題:ContentProvider

ContentProvider初始化時機

在之前的# Android 11源碼分析: Application的初始化流程一文中我曾提到過,後面會專門有一篇文章介紹ContentProvider初始化。 當時是在講到在H接受到初始化Application的消息時觸發handleBindApplication方法的執行,內部進行Application的創建和執行onCreate,再來看看這段代碼

android.app.ActivityThread
......
try {
1 通過反射獲取Instrumentation
final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate instrumentation "
+ data.instrumentationName + ": " + e.toString(), e);
}
......
Application app;
......
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
2.創建Application(內部也是反射)
app = data.info.makeApplication(data.restrictedBackupMode, null);
......
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
本文關注點:初始化ContentProvider
installContentProviders(app, data.providers);
}
}
try {
3.初始化Application
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}finally {
......
}
}
......
}
複制代碼

可以看到,執行初始化ContentProvider的時機在makeApplication和callApplicationOnCreate直接,那最起碼可以得出一個結論:ContentProvider初始化的時機在Application的onCreate之前

有問題麼,沒有問題。

那麼問題來了,啥叫最起碼得出一個結論,這不是還有別的結論嗎? 是的。因為前面還提到了在makeApplication之後啊。 再去看看Instrumentatio創建Application做了啥和有關的事就知道了

android.app.Instrumentation
public Application newApplication(ClassLoader cl, String className, Context context)
throws InstantiationException, IllegalAccessException,
ClassNotFoundException {
Application app = getFactory(context.getPackageName())
.instantiateApplication(cl, className);
app.attach(context);
return app;
}
複制代碼

之前並沒有提到app.attach(context)這句代碼,今天就去一探究竟。

android.app.Application
final void attach(Context context) {
attachBaseContext(context);
mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}
複制代碼

對的。。我說了這麼多,就是下最後的結論:ContentProvider初始化的時機在Application的attachBaseContext之後,在onCreate之前

ContentProvider初始化做了什麼

想要弄明白ContentProvider初始化做了什麼,那就需要去installContentProviders方法看一看了。

android.app.ActivityThread
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
遍曆ProviderInfo列錶
for (ProviderInfo cpi : providers) {
if (DEBUG_PROVIDER) {
StringBuilder buf = new StringBuilder(128);
buf.append("Pub ");
buf.append(cpi.authority);
buf.append(": ");
buf.append(cpi.name);
Log.i(TAG, buf.toString());
}
各自啟動
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
啟動後放入數組中
results.add(cph);
}
}
try {
將數組傳給AMS
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
複制代碼

installContentProviders完成了通過installProvider方法完成ContentProvider的啟動,並且將啟動了的ContentProvider放在了數組中,傳遞給了AMS,AMS內部會將他們存起來,這樣外部調用者就可以直接從AMS中獲取ContentProvider了。

android.app.ActivityThread
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
ContentProvider localProvider = null;
......
localProvider = packageInfo.getAppFactory()
.instantiateProvider(cl, info.name);
localProvider.attachInfo(c, info);
......
}
複制代碼

通過反射創建ContentProvider對象,調用attachInfo方法

android.content.ContentProvider
public void attachInfo(Context context, ProviderInfo info, boolean testing) {
......
ContentProvider.this.onCreate();
}
複制代碼

attachInfo方法會調用上參數的同名方法,執行ContentProvider的onCreate,這也就意味著ContentProvider的啟動完成了

使用ContentProvider初始化項目需要注意的地方

  1. 因為ContentProvider的onCreate() 在Application 的 onCreate() 方法之前,所以如果有其他模塊的初始化,需要考慮是否會有初始化順序的問題。
  2. 有一些第三方庫,例如友盟,他其實是要求在Application的onCreat方法中進行初始化的,所以使用ContentProvider初始化的不行的。其實利用門面模式,封裝一個UmManager,在主項目Application中初始化
版权声明:本文为[ZhaoYun]所创,转载请带上原文链接,感谢。 https://gsmany.com/2021/08/20210815181419040Q.html