如何在Android的ContentProvider中处理Room功能?

问题描述 投票:0回答:1

我正在尝试通过

SQLite
中的
Room
库提供来自
ContentProvider
的数据,并且我的应用程序使用
Hilt
进行依赖项注入。

@Entity(tableName = "files")
data class FileEntity(
    @PrimaryKey(autoGenerate = false)
    val url: String,
    val fileName: String
)
@Dao
interface FilesDao {
    @Query(value = "SELECT * FROM files ORDER BY fileName")
    suspend fun getFiles(): List<FileEntity>
}
@Database(entities = [FileEntity::class], version = 1, exportSchema = false)
abstract class AppDatabase : RoomDatabase() {
    abstract val filesDao: FilesDao
}
@Module
@InstallIn(SingletonComponent::class)
object RoomModule {
    @Provides
    @Singleton
    fun provideAppDatabase(
        @ApplicationContext context: Context
    ): AppDatabase = Room.databaseBuilder(
        context, AppDatabase::class.java, "app_database"
    ).build()

    @Provides
    @Singleton
    fun provideFilesDao(
        appDatabase: AppDatabase
    ): FilesDao = appDatabase.filesDao
}

通过上面的代码,我可以在

FilesDao
中接收
DataSource class
的实例,并通过
suspended functions
正常拨打电话。例如:

data class FileModel(
    val url: String,
    val fileName: String
)
interface FilesDataSource {
    suspend fun getFiles(): List<FileModel>
}

class FilesDataSourceImpl @Inject constructor(
    private val filesDao: FilesDao
) : FilesDataSource {
    override suspend fun getFiles(): List<FileModel>{
        val filesEntity = filesDao.getFiles()
        return filesEntity.toFilesModel()
    }

    private fun List<FileEntity>.toFilesModel() = map { fileEntity ->
        FileModel(
            url = fileEntity.url,
            fileName = fileEntity.fileName
        )
    }
}

要使用

Hilt
通过依赖注入将实例传递给
ContentProvider
我知道有必要创建一个自定义
EntryPoint
。例如:

@EntryPoint
@InstallIn(SingletonComponent::class)
interface ContentProviderEntryPoint {
    // var appDatabase: AppDatabase
    var filesDao: FilesDao
}

ContentProvider
中,到目前为止我有以下代码:

class MyProvider : ContentProvider() {
    private val appContext: Context by lazy {
        context?.applicationContext ?: throw IllegalStateException()
    }

    private val files: List<FileModel> by lazy {
        val hiltEntryPoint = EntryPointAccessors.fromApplication(
            context = appContext,
            entryPoint = ContentProviderEntryPoint::class.java
        )

        hiltEntryPoint.filesDao.getFiles() // this is a suspend function and I have no idea how to deal...
    }

    override fun onCreate(): Boolean {
        return true
    }

    override fun query(
        uri: Uri,
        projection: Array<out String>?,
        selection: String?,
        selectionArgs: Array<out String>?,
        sortOrder: String?
    ): Cursor {
        TODO()
    }

    override fun getType(uri: Uri): String {
        TODO()
    }

    override fun openAssetFile(
        uri: Uri,
        mode: String
    ): AssetFileDescriptor? {
        TODO()
    }

    override fun insert(
        uri: Uri,
        values: ContentValues?
    ): Uri = throw UnsupportedOperationException()

    override fun delete(
        uri: Uri,
        selection: String?,
        selectionArgs: Array<out String>?
    ): Int = throw UnsupportedOperationException()

    override fun update(
        uri: Uri,
        values: ContentValues?,
        selection: String?,
        selectionArgs: Array<out String>?
    ): Int = throw UnsupportedOperationException()
}

现在我的疑问,如何处理

suspended function
里面的
ContentProvider

补充:
我见过一些在

DAO class
中创建新函数(未暂停)以返回
Cursor
的示例。例如:

@Dao
interface FilesDao {
    @Query(value = "SELECT * FROM files")
    fun getFilesCursor(): Cursor

    // other suspend functions for datasource layer
}

这样我就可以在

ContentProvider
中调用新函数,但是 Room 会抛出异常:

class MyProvider : ContentProvider() {
    private val appContext: Context by lazy {
        context?.applicationContext ?: throw IllegalStateException()
    }

    private val filesCursor: Cursor by lazy {
        val hiltEntryPoint = EntryPointAccessors.fromApplication(
            context = appContext,
            entryPoint = ContentProviderEntryPoint::class.java
        )

        hiltEntryPoint.filesDao.getFilesCursor()
    }

    override fun onCreate(): Boolean {
        println(filesCursor) // just to see the output, but this throw an IllegalStateException
        return true
    }

    // ...
}

java.lang.IllegalStateException:无法在主线程上访问数据库,因为它可能会长时间锁定 UI。

我的

ContentProvider
目标是为WhatsApp提供贴纸(文件)。我已经有一个应用程序的工作版本,可以直接从 JSON 文件 读取内容,遵循与
Hilt
相同的逻辑,问题是现在我正在迁移到使用
Room
I'我无法处理这种情况。
    

android kotlin android-contentprovider
1个回答
0
投票
suspended functions

runBlocking

但是您应该从 
override fun query( uri: Uri, projection: Array<out String>?, selection: String?, selectionArgs: Array<out String>?, sortOrder: String? ): Cursor { return runBlocking { // your code here } }

调用

query
,因为
CoroutineScope(Dispatchers.IO)
会中断地阻塞当前线程,直到其完成
    

© www.soinside.com 2019 - 2024. All rights reserved.