我的第一个观察者调用正确,但另一个观察者在将数据插入 kotlin 中的房间数据库后没有被调用 android

My first observer called correctly but another was not called after inserted data to room database in kotlin android

在应用程序中,我从网络和观察者更改方法中获取数据,将该数据插入本地数据库。没关系。但是在插入数据库后,我的第二个观察者没有调用所以我的 UI 不会更新。

ManActivity.class

class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: MainViewModel
    private lateinit var adapter: MainAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(layout.activity_main)
        setupViewModel()
        setupUI()
        setupObservers()
        setupObservers2()
    }


    private fun setupViewModel() {
        viewModel = ViewModelProviders.of(
            this,
            ViewModelFactory(ApiHelper(RetrofitBuilder.apiService))
        ).get(MainViewModel::class.java)
    }

    private fun setupUI() {
        recyclerView.layoutManager = LinearLayoutManager(this)
        adapter = MainAdapter(arrayListOf())
        recyclerView.addItemDecoration(
            DividerItemDecoration(
                recyclerView.context,
                (recyclerView.layoutManager as LinearLayoutManager).orientation
            )
        )
        recyclerView.adapter = adapter
    }

    private fun setupObservers() {
        viewModel.getUsers().observe(this, Observer {

            //viewModel.getUserFromWeb()
            it?.let { resource ->
               when (resource.status) {
                    SUCCESS -> {
                        Log.d("MYLOG","MyAPIChange success")
                        recyclerView.visibility = View.VISIBLE
                        progressBar.visibility = View.GONE
                        resource.data?.let {

                               users ->  viewModel.setUserListToDB(this,users)
                               //sleep(1000)
                           }

                    }
                    ERROR -> {
                        recyclerView.visibility = View.VISIBLE
                        progressBar.visibility = View.GONE
                        Log.d("MYLOG","MyAPIChange error")
                        Toast.makeText(this, it.message, Toast.LENGTH_LONG).show()
                    }
                    LOADING -> {
                        Log.d("MYLOG","MyAPIChange loading")
                        progressBar.visibility = View.VISIBLE
                        recyclerView.visibility = View.GONE
                    }
                }
            }
        })

    }

    private fun setupObservers2() {
        viewModel.getUserFromDB(this).observe(this, Observer {
                users -> retrieveList(users)
            Log.d("MYLOG","..MyDBChange")
        })
    }

    private fun retrieveList(users: List<User>) {
        adapter.apply {
            addUsers(users)
            notifyDataSetChanged()
        }
    }
}

MyViewModel.class

class MainViewModel(private val mainRepository: MainRepository) : ViewModel() {

    //lateinit var tempUser : MutableLiveData<List<User>>
    fun getUsers() = liveData(Dispatchers.IO) {
        emit(Resource.loading(data = null))
        try {
            emit(Resource.success(data = mainRepository.getUsers()))
        } catch (exception: Exception) {
            emit(Resource.error(data = null, message = exception.message ?: "Error Occurred!"))
        }
        //emit(mainRepository.getUsers())   //direct call
    }

    fun getUserFromDB(context: Context) = liveData(Dispatchers.IO) {
        emit(mainRepository.getUserList(context))
    }

    fun setUserListToDB(context: Context, userList: List<User>) {

        /*GlobalScope.launch {
            mainRepository.setUserList(context, userList)
        }*/

        CoroutineScope(Dispatchers.IO).launch {
            mainRepository.setUserList(context, userList)
        }

    }
}

MyRepository.class

class MainRepository(private val apiHelper: ApiHelper) {

    suspend fun getUsers() = apiHelper.getUsers()    // get from web

    companion object {

        var myDatabase: MyDatabase? = null

        lateinit var userList: List<User>

        fun initializeDB(context: Context): MyDatabase {
            return MyDatabase.getDataseClient(context)
        }

        /*fun insertData(context: Context, username: String, password: String) {

            myDatabase = initializeDB(context)

            CoroutineScope(Dispatchers.IO).launch {
                val loginDetails = User(username, password)
                myDatabase!!.myDao().InsertData(loginDetails)
            }

        }*/
    }
    //fun getUserList(context: Context, username: String) : LiveData<LoginTableModel>? {

    suspend fun getUserList(context: Context) : List<User> {

        myDatabase = initializeDB(context)
        userList = myDatabase!!.myDao().getUserList()
        Log.d("MYLOG=", "DBREAD"+userList.size.toString())


         return userList
    }

    fun setUserList(context: Context,userList: List<User>){

        myDatabase = initializeDB(context)

        /*CoroutineScope(Dispatchers.IO).launch {
            myDatabase!!.myDao().InsertAllUser(userList)
            Log.d("MYLOG","MyDBInserted")
        }*/
        myDatabase!!.myDao().InsertAllUser(userList)
        Log.d("MYLOG","MyDBInserted")
        /*val thread = Thread {
            myDatabase!!.myDao().InsertAllUser(userList)
        }
        Log.d("MYLOG","MyDBInserted")
        thread.start()*/
    }
}

DAO class

@Dao
interface DAOAccess {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun InsertAllUser(userList: List<User>)

//    @Query("SELECT * FROM User WHERE Username =:username")
//    fun getLoginDetails(username: String?) : LiveData<LoginTableModel>

    @Query("SELECT * FROM User")
    suspend fun getUserList() : List<User>
}

RetrofitBuilder

object RetrofitBuilder {

    private const val BASE_URL = "https://5e510330f2c0d300147c034c.mockapi.io/"

    private fun getRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    val apiService: ApiService = getRetrofit().create(ApiService::class.java)
}

请你知道我在这里做错了什么以及为什么在插入到 db 后没有调用第二个观察者

实际上它是在屏幕启动时调用的,但当时数据未插入,因此列表大小为 0,插入数据后此方法不会再次调用。但是一旦我关闭应用程序并再次启动,数据将在启动时显示 bcoz,此方法调用和数据得到

我没有足够的声誉来评论,所以我只是在这个答案中提出一个建议:

Suggestion/Solution

Room 支持开箱即用的 LiveData。所以在你的 DAO 中你可以改变

suspend fun getUserList() : List<User>

suspend fun getUserList() : LiveData<List<User>>

然后在您的存储库中调整为

suspend fun getUserList(context: Context) : LiveData<List<User>> {

        myDatabase = initializeDB(context)
        userList = myDatabase!!.myDao().getUserList()
        Log.d("MYLOG=", "DBREAD"+userList.value.size.toString())


         return userList
    }

并在 ViewModel 中

fun getUserFromDB(context: Context) = mainRepository.getUserList(context))
    

通过这些调整,我认为它应该起作用。

说明

您在这里使用了 liveData 协程构建器

fun getUserFromDB(context: Context) = liveData(Dispatchers.IO) {
        emit(mainRepository.getUserList(context))
    }

据我了解,此生成器旨在执行某些 asynchronous/suspend 任务,一旦此任务完成,您创建的 liveData 就会发出结果。这意味着您只接收一次用户列表的状态,一次将列表发送给观察者,然后这个 liveData 就完成了。它不会一直观察数据库中列表的变化。

这就是为什么它非常适合观察 API 调用(你想等到调用完成并发出一次响应),而不是观察数据库状态(你想始终观察数据库中的用户列表,并在列表更改时向观察者发出更改)