带有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 的 onCreateinitUriMatcher 的代码。

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 了,所以我没有测试过这种情况。请注意,这对概览屏幕与“强制停止”行为相关联的设备没有帮助,因为这会终止您的所有进程。