带有ContentProvider代码的应用被kill时找不到ContentProvider信息
Failed to find ContentProvider info when the app with the ContentProvider code is killed
我有两个应用程序:一个主应用程序和一个消费者应用程序。我的消费者应用程序将使用 ContentResolver
访问主应用程序的本地数据库。当 主应用程序和消费者应用程序 都是 运行 时,一切正常。但是,一旦我 终止主应用程序 ,消费者应用程序 就无法 访问本地数据库。报错信息如下。
E/ActivityThread: Failed to find provider info for com.mobile.githubuser.provider
我已经检查了两个应用程序的清单,它们都是正确的(否则,当它们都是 运行 时,消费者应用程序将无法访问主应用程序)。不过,这些是我的两个应用程序的清单。
主应用程序清单:
<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 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>
我认为问题可能是我的 ContentProvider
class 引起的。这是我的主应用程序中我的提供商 class 的 onCreate
、init
和 UriMatcher
的代码。
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)
}
}
}
这是什么问题,我该如何解决?
通常,Android 会自动管理 ContentProvider
的进程,在需要时启动它,在不再需要时终止它。如果进程意外终止,例如由于崩溃,客户端可能会出错,这与 Web 服务器崩溃时 Web 客户端可能会出错没有什么不同。
从概览屏幕中删除应用程序的具体行为因设备而异。在某些情况下,它可能只是删除任务(即返回堆栈)并单独留下进程。在许多情况下,它将终止与该任务关联的进程。而且,在某些情况下,它的行为就像用户在该应用程序的设置屏幕上使用了“强制停止”一样。因此,如果用户从包含正在使用的 ContentProvider
的概览屏幕中删除应用,结果会有所不同,但可能会导致这些活跃客户端出现错误。
您可以尝试通过使用 <provider>
元素上的 android:process
属性让提供程序处于单独的进程中来尽量减少这种影响。从好的方面来说,这降低了从概览屏幕中删除应用程序会影响该特定过程的可能性。不利的一面是,这意味着包含该提供者的应用程序的其余部分也需要使用 IPC 与该提供者一起工作,就像应用程序外部的客户端所做的那样。就个人而言,我已经有一段时间没有在需要 long-lived 客户端的地方编写 ContentProvider
了,所以我没有测试过这种情况。请注意,这对概览屏幕与“强制停止”行为相关联的设备没有帮助,因为这会终止您的所有进程。
我有两个应用程序:一个主应用程序和一个消费者应用程序。我的消费者应用程序将使用 ContentResolver
访问主应用程序的本地数据库。当 主应用程序和消费者应用程序 都是 运行 时,一切正常。但是,一旦我 终止主应用程序 ,消费者应用程序 就无法 访问本地数据库。报错信息如下。
E/ActivityThread: Failed to find provider info for com.mobile.githubuser.provider
我已经检查了两个应用程序的清单,它们都是正确的(否则,当它们都是 运行 时,消费者应用程序将无法访问主应用程序)。不过,这些是我的两个应用程序的清单。
主应用程序清单:
<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 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>
我认为问题可能是我的 ContentProvider
class 引起的。这是我的主应用程序中我的提供商 class 的 onCreate
、init
和 UriMatcher
的代码。
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)
}
}
}
这是什么问题,我该如何解决?
通常,Android 会自动管理 ContentProvider
的进程,在需要时启动它,在不再需要时终止它。如果进程意外终止,例如由于崩溃,客户端可能会出错,这与 Web 服务器崩溃时 Web 客户端可能会出错没有什么不同。
从概览屏幕中删除应用程序的具体行为因设备而异。在某些情况下,它可能只是删除任务(即返回堆栈)并单独留下进程。在许多情况下,它将终止与该任务关联的进程。而且,在某些情况下,它的行为就像用户在该应用程序的设置屏幕上使用了“强制停止”一样。因此,如果用户从包含正在使用的 ContentProvider
的概览屏幕中删除应用,结果会有所不同,但可能会导致这些活跃客户端出现错误。
您可以尝试通过使用 <provider>
元素上的 android:process
属性让提供程序处于单独的进程中来尽量减少这种影响。从好的方面来说,这降低了从概览屏幕中删除应用程序会影响该特定过程的可能性。不利的一面是,这意味着包含该提供者的应用程序的其余部分也需要使用 IPC 与该提供者一起工作,就像应用程序外部的客户端所做的那样。就个人而言,我已经有一段时间没有在需要 long-lived 客户端的地方编写 ContentProvider
了,所以我没有测试过这种情况。请注意,这对概览屏幕与“强制停止”行为相关联的设备没有帮助,因为这会终止您的所有进程。