ContentProvider Startup Order and the CursorWindow Behind ContentResolver
ContentProvider Startup Order and the CursorWindow Behind ContentResolver
A ContentProvider is usually described as a way to share data between apps, but it has a second role that explains a surprising amount of library behavior: the system creates and calls onCreate() on every provider declared in your manifest before it calls Application.onCreate(). That single ordering fact is why Firebase, WorkManager, and AndroidX App Startup can initialize themselves without any code in your Application class. On the read path, a ContentResolver query does not copy rows across a Binder call; it hands back a Cursor backed by a CursorWindow, a fixed size region of shared memory. By the end of this lesson, you will be able to:
- Trace where
ContentProvider.onCreate()runs in the app launch sequence relative toApplication.onCreate(). - Describe how
installContentProvidersturns a manifest<provider>entry into a live provider object. - Explain how AndroidX App Startup and Firebase use a manifest provider as an auto initialization hook, and what it costs.
- Describe how a
ContentResolverquery reaches a provider in another process and what aCursorWindowis. - Explain why a query over a very large table does not exhaust memory and why you must close a
Cursor.
Where providers fit in app startup
When the system starts your process, ActivityThread.handleBindApplication runs the application bring up sequence. The relevant portion, slightly trimmed:
// app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
app = data.info.makeApplicationInner(data.restrictedBackupMode, null);
...
mInitialApplication = app;
...
// don't bring up providers in restricted mode; they may depend on the
// app's custom Application class
if (!data.restrictedBackupMode) {
if (!ArrayUtils.isEmpty(data.providers)) {
installContentProviders(app, data.providers);
}
}
...
mInstrumentation.callApplicationOnCreate(app);
The order here is the whole point. makeApplicationInner constructs your Application subclass and calls attachBaseContext() on it, so by the time the next lines run the Application object exists and has a Context. Then installContentProviders brings up every provider in data.providers, which is the list of ProviderInfo the system handed this process based on your merged manifest. Only after that does callApplicationOnCreate(app) run, which is what eventually calls your Application.onCreate().
So the lifecycle is: Application constructor and attachBaseContext(), then ContentProvider.onCreate() for each declared provider, then Application.onCreate(). A provider's onCreate() can call getContext() and get the application Context, but it cannot assume anything your Application.onCreate() did has happened yet.
There is one caveat visible in the comment: in restricted backup mode the providers are deliberately not started, because they might depend on a custom Application class that backup mode does not instantiate.
From manifest entry to onCreate()
installContentProviders walks the provider list and, for each, calls installProvider, then publishes the results to the Activity Manager:
private void installContentProviders(Context context, List<ProviderInfo> providers) {
final ArrayList<ContentProviderHolder> results = new ArrayList<>();
for (ProviderInfo cpi : providers) {
ContentProviderHolder cph = installProvider(context, null, cpi,
false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
if (cph != null) {
cph.noReleaseNeeded = true;
results.add(cph);
}
}
ActivityManager.getService().publishContentProviders(getApplicationThread(), results);
}
installProvider resolves the right Context for the provider's package, then instantiates the class by reflection through the app's AppComponentFactory and attaches it:
localProvider = packageInfo.getAppFactory().instantiateProvider(cl, info.name);
provider = localProvider.getIContentProvider();
...
localProvider.attachInfo(c, info);
attachInfo is where onCreate() actually fires. Inside ContentProvider.attachInfo, after it stores the Context and reads the permissions and authority out of the ProviderInfo, the last line is:
if (mContext == null) {
mContext = context;
...
setAuthorities(info.authority);
...
ContentProvider.this.onCreate();
}
Two things follow from this. First, onCreate() runs on the main thread, as part of process startup, synchronously, before your app's first Activity is created. Second, it runs whether or not anything ever queries the provider. Declaring a <provider> in the manifest is enough; the system loads it eagerly. publishContentProviders then registers the live providers with ActivityManagerService so that other processes can look them up by authority later.
This interview continues for subscribers
Subscribe to Dove Letter for full access to exclusive interviews about Android and Kotlin development.
Become a Sponsor