我正在尝试通过
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'我无法处理这种情况。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)
会中断地阻塞当前线程,直到其完成