概述
ContentProvider的啓動其實是在App啓動時就自動啓動的,還不知道APP啓動流程的,推薦看一下Android App啓動過程,我們知道當一個App啓動時,經歷了以下步驟
- 首先是點擊App圖標,此時是運行在
Launcher
進程,通過ActivityManagerService
Binder IPC的形式向system_server
進程發起startActivity
的請求 system_server
進程接收到請求後,通過Process.start
方法向zygote
進程發送創建進程的請求zygote
進程fork
出新的子進程,即App
進程- 然後進入
ActivityThread.main
方法中,這時運行在App
進程中,通過ActivityManagerService
Binder IPC的形式向system_server
進程發起attachApplication
請求 system_server
接收到請求後,進行一些列準備工作後,再通過Binder IPC向App
進程發送scheduleLaunchActivity
請求App
進程binder線程(ApplicationThread)
收到請求後,通過Handler
向主線程發送LAUNCH_ACTIVITY
消息- 主線程收到Message後,通過反射機制創建目標
Activity
,並回調Activity
的onCreate
而我們的ContentProvider的啓動
是在第四步的attachApplication
中請求的開始的,下面我們就具體看源碼分析
ActivityManagerService.java
public final void attachApplication(IApplicationThread thread, long startSeq) {
synchronized (this) {
int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
Binder.restoreCallingIdentity(origId);
}
}
private final boolean attachApplicationLocked(IApplicationThread thread,
int pid) {
....
thread.bindApplication(processName, appInfo, providers,
app.instr.mClass,
profilerInfo, app.instr.mArguments,
app.instr.mWatcher,
app.instr.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(getGlobalConfiguration()), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial);
}
這裏調用了thread
的bindApplication
方法,thread
的類型是IApplicationThread
,是一個binder
用於跨進程通信,實現類是ActivityThread
的內部類ApplicationThread
類
ApplicationThread.java
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial, boolean autofillCompatibilityEnabled) {
...
sendMessage(H.BIND_APPLICATION, data);
}
這個方法其實最後發送了一個BIND_APPLICATION
消息給ActivityThread
的內部類H
ActivityThread.java
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
這個方法調用了handleBindApplication
方法
private void handleBindApplication(AppBindData data) {
...
final InstrumentationInfo ii;
...
// 創建 mInstrumentation 實例
if (ii != null) {
...
//創建ContextImpl
final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
try {
//創建mInstrumentation實例
final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
...
}
...
} else {
mInstrumentation = new Instrumentation();
}
...
Application app;
...
try {
...
// 創建 Application 實例
app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
//啓動ContentProvider
installContentProviders(app, data.providers);
mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
}
try {
//調用Application的onCreate
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
...
}
} finally {
...
}
...
}
我們從上向下分析這個方法
- 首先創建了
ContextImpl
對象 - 然後創建了
mInstrumentation
對象 - 接着創建了
Application
對象 - 然後啓動了
ContentProvider
- 最後調用了
Application
對象的onCreate
我們發現其實在調用Application
的onCreate
之前,就已經啓動了ContentProvider
,這個也可以作爲啓動優化
的一部分,如果不需要ContentProvider
的話建議刪除,因爲他會自動啓動,之前我用TraceView
測試過,大概佔用5ms
,但是蚊子再小也是肉啊
我們繼續分析installContentProviders
方法
private void installContentProviders(
Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
//註釋1
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());
}
//註釋2
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
try {
//註釋3
ActivityManager.getService().publishContentProviders(
getApplicationThread(), results);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
- 註釋1處,遍歷當前應用程序的
ProviderInfo
列表,得到每個ContentProvider
的ProviderInfo
(存儲ContentProvider
的信息) - 註釋2處,調用
installProvider
來啓動ContentProvider
- 通過
AMS
的publishContentProviders
方法,將這些ContentProvider
儲存到AMS
的mProviderMap
中,起到緩存作用,防止重複調用
我們繼續分析installProvider
方法
private ContentProviderHolder installProvider(Context context,
ContentProviderHolder holder, ProviderInfo info,
boolean noisy, boolean noReleaseNeeded, boolean stable) {
···
//註釋1
final java.lang.ClassLoader cl = c.getClassLoader();
LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
localProvider = cl.loadClass(className).newInstance();
provider = localProvider.getIContentProvider();
···
//註釋2
localProvider.attachInfo(c, info);
} catch (java.lang.Exception e) {
if (!mInstrumentation.onException(null, e)) {
throw new RuntimeException(
"Unable to get provider " + info.name
+ ": " + e.toString(), e);
}
}
- 註釋1處,通過反射來創建
ContentProvider
- 註釋2處,調用了
ContentProvider
的attachInfo
方法
private void attachInfo(Context context, ProviderInfo info, boolean testing) {
mNoPerms = testing;
if (info != null) {
setReadPermission(info.readPermission);
setWritePermission(info.writePermission);
setPathPermissions(info.pathPermissions);
mExported = info.exported;
mSingleUser = (info.flags & ProviderInfo.FLAG_SINGLE_USER) != 0;
setAuthorities(info.authority);
}
ContentProvider.this.onCreate();
}
}
在這個方法中調用了ContentProvider
的onCreate
,到這裏ContentProvider
的啓動就完成了
參考:《Android進階解密》