必须在主线程异常上调用方法 addObserver,同时将数据插入房间数据库

Method addObserver must be called on the main thread Exception, While inserting data to room database

我正在尝试使用 kotlin 协程将数据插入房间数据库。但我总是得到一个例外 java.lang.IllegalStateException: Method addObserver must be called on the main thread 但是我在这段代码中没有观察者,插入调用是从启动时使用 Dispatchers IO

调用的

DocumentDao.kt

@Dao
interface DocumentDao {
        @Insert
        suspend fun insertDocument(document: Document): Long
    }

Repository.kt

class Repository@Inject constructor(val db: MyDB) {
    suspend fun demoInsert(
        uri: String,
        albumId: Long
    ): Long {
        val newDoc = Document(0, albumId, rawUri = uri)
        return db.documentDao().insertDocument(newDoc)
    }
}

MyViewModel.kt

@HiltViewModel
class MyViewModel@Inject constructor(val repo: Repository) : ViewModel() {

    suspend fun demoInsert(
        uri: String,
        albumId: Long
    ): Long {

        return repo.demoInsert(uri, albumId)
    }
}

MyFrag.kt

@AndroidEntryPoint
class MyFrag: Fragment() {
 val viewModel: MyViewModel by viewModels()
....
....
   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
         binding.insert.setOnClickListener {
               lifecycleScope.launch(Dispatchers.IO) {
                  val res =  viewModel.demoInsert("test", Random.nextLong(500))
                 Log.d(TAG, "onViewCreated: $res")
           }
     }
    
........
.......
}

}

这段代码有什么问题?请帮助我

我不确定,但您可以使用 Main Dispatcher 在侦听器内部启动 coroutine,然后在 DB 函数内部使用 withContext 来更改上下文。

正在创建视图模型:

val viewModel: MyViewModel by viewModels()

将导致延迟创建。当您第一次访问您的对象时,将执行真实对象的创建。这发生在里面:

lifecycleScope.launch(Dispatchers.IO) {
     val res =  viewModel.demoInsert("test", Random.nextLong(500))
     Log.d(TAG, "onViewCreated: $res")
}

并且由于方法 viewModels<>() 的实现看起来像这样:

@MainThread
public inline fun <reified VM : ViewModel> Fragment.viewModels(
    noinline ownerProducer: () -> ViewModelStoreOwner = { this },
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> = createViewModelLazy(VM::class, { ownerProducer().viewModelStore }, factoryProducer)

您正在获得

Method addObserver must be called on the main thread

你应该可以用类似的方法解决这个问题。

lifecycleScope.launch(Dispatchers.IO) {
     val res =  withContext(Dispatchers.Main + lifecycleScope.coroutineContext){}.demoInsert("test", Random.nextLong(500))
     Log.d(TAG, "onViewCreated: $res")
}

我遇到了同样的问题,我是这样解决的:

 private fun insertAllItemsInDb(data : List<PostResponse>){
         val listPost = data.map { it.toUI() }
         val scope = CoroutineScope(Job() + Dispatchers.Main)
         scope.launch {
             localViewModel.insertAllPosts(listPost)
         }
     }

视图模型:

fun insertAllPosts(posts: List<PostItem>) {
        viewModelScope.launch {
            dbRepository.insertAllPosts(posts)
        }
}

MyViewModel.kt

    @HiltViewModel
class MyViewModel@Inject constructor(val repo: Repository) : ViewModel() {

    suspend fun demoInsert(
        uri: String,
        albumId: Long
    ): Long {

 viewModelScope.launch {
            repo.demoInsert(uri, albumId)
        }
       
    }
}

MyFrag.kt

@AndroidEntryPoint
class MyFrag: Fragment() {

 val viewModel: MyViewModel by viewModels()

   override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
         binding.insert.setOnClickListener {
         
               lifecycleScope.launch(Dispatchers.Main) {
                  
                  viewModel.demoInsert("test", Random.nextLong(500))
                 
                 }
               
           }
           
     }
   
}