I have two apps: a main app and a consumer app. My consumer app will access the local database of the main app using ContentResolver
. Everything works fine when both the main app and consumer app are running. However, as soon as I kill the main app, the consumer app cannot access the local database. The error message is as follows.
E/ActivityThread: Failed to find provider info for com.mobile.githubuser.provider
I have checked the manifests of both apps and they're correct (otherwise, the consumer app wouldn't have been able to access the main app when they're both running). Nevertheless, these are the manifests of both my apps.
Manifest of the main app:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.mobile.githubuser">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<permission android:name="com.mobile.githubuser.READ_DATABASE" android:protectionLevel="normal" />
<permission android:name="com.mobile.githubuser.WRITE_DATABASE" android:protectionLevel="normal" />
<application
...>
<provider
android:name="com.mobile.githubuser.provider.FavoriteUserProvider"
android:authorities="com.mobile.githubuser.provider"
android:enabled="true"
android:exported="true"
android:readPermission="com.mobile.githubuser.READ_DATABASE"
android:writePermission="com.mobile.githubuser.WRITE_DATABASE" />
...
Manifest of the consumer app.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.mobile.githubuser.consumerapp">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="com.mobile.githubuser.READ_DATABASE" />
<uses-permission android:name="com.mobile.githubuser.WRITE_DATABASE" />
<application
...>
...
</application>
</manifest>
I think that the problem might be caused by my ContentProvider
class. Here's the code for the onCreate
, init
, and UriMatcher
of my provider class in my main app.
class FavoriteUserProvider : ContentProvider() {
private lateinit var favoriteUserDao: FavoriteUserDao
override fun onCreate(): Boolean {
context?.let {
val context = it
favoriteUserDao = FavoriteUserRoomDatabase.getDatabase(context).favoriteUserDao()
}
return true
}
override fun query(
uri: Uri, projection: Array<String>?, selection: String?,
selectionArgs: Array<String>?, sortOrder: String?
): Cursor? {
...
}
override fun getType(uri: Uri): String? {
...
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
...
}
override fun update(
uri: Uri, values: ContentValues?, selection: String?,
selectionArgs: Array<String>?
): Int {
...
}
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
...
}
companion object {
private const val FAVORITE_USER_LIST = 1
private const val FAVORITE_USER_ITEM = 2
private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
init {
uriMatcher.addURI(AUTHORITY, FavoriteUserColumns.TABLE_NAME, FAVORITE_USER_LIST)
uriMatcher.addURI(AUTHORITY, "${FavoriteUserColumns.TABLE_NAME}/*", FAVORITE_USER_ITEM)
}
}
}
What is the problem here and how can I fix it?
Normally, Android manages the process for the ContentProvider
automatically, starting it when needed and terminating it when it is no longer needed. If the process terminates unexpectedly, such as due to a crash, clients may get errors, no different than Web clients may get errors if a Web server crashes.
The exact behavior of removing an app from the overview screen varies by device. In some cases, it may just remove the task (i.e., back stack) and leave processes alone. In many cases, it will terminate the process associated with that task. And, in a few cases it will behave as if the user used "Force Stop" on the Settings screen for that app. As a result, if the user removes an app from the overview screen that contains a ContentProvider
that is actively being used, the results will vary but may result in errors in those active clients.
You can try to minimize this effect by having the provider be in a separate process, using the android:process
attribute on the <provider>
element. On the plus side, this reduces the likelihood that removing the app from the overview screen will affect that particular process. On the minus side, it means that the rest of the app containing that provider also needs to use IPC work with that provider, as clients outside of the app do. Personally, I have not written a ContentProvider
where I needed a long-lived client in quite some time, so I have not tested this scenario. And note that this will not help on those devices where the overview screen is tied to "Force Stop" behavior, as that should terminate all of your processes.