建筑组件模式-Room Database中的LiveDate始终为空,从不刷新

时间:2018-07-12 13:23:18

标签: android android-room android-architecture-components android-livedata

我正在尝试使用体系结构组件来实现应用程序。我基于此google示例项目: https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/repository/UserRepository.kt

但是我遇到了从存储库中以LiveData身份获取的用户始终为null的问题,并且在观察的同时,我仅收到一次null值的通知。

UI

 private fun getUserProfile() {
        myAccountViewModel.getUserProfile().observe(this, Observer { resource ->
            Toast.makeText(this, "User is: ${resource?.data?.id ?: 0}", Toast.LENGTH_LONG).show()
            println("User profile: ${resource?.data}")
        })
    }

查看模型

class UserProfileViewModel
@Inject constructor(val repository: UserProfileRepository) : ViewModel() {

    private var userProfile : LiveData<Resource<UserProfile>>

    init {
        userProfile = repository.loadUserProfile()
    }

    fun getUserProfile() : LiveData<Resource<UserProfile>> {
        return userProfile
    }
}

存储库

 fun loadUserProfile() : LiveData<Resource<UserProfile>> {

       return object : CachedResource<UserProfile, UserProfile>(appExecutors) {
           override fun saveCallResult(item: UserProfile) {
               dao.insert(item)
           }

           override fun shouldFetch(data: UserProfile?): Boolean {
               // always fetch new user profile from web
               return true
           }

           override fun loadFromDb(): LiveData<UserProfile> {
               // load user profile based on identifier
               // it is stored with Token in Shared Preferences
               val userId = authRepository.getAuthToken().value?._id
               if (userId != null) {
                   return dao.load(userId)
               }

               return MutableLiveData<UserProfile>()
           }

           override fun createCall(): LiveData<Response<UserProfile>> {
                return api.getUserProfile()
           }

       }.asLiveData()
    }

DAO

@Dao
interface UserProfileDao {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun insert(userProfile: UserProfile)

    @Query("SELECT * FROM user_profile WHERE id = :id")
    fun load(id: String) : LiveData<UserProfile>

    @Query("SELECT * FROM user_profile")
    fun loadAll() : LiveData<List<UserProfile>>
}

CachedResource的实现-基于来自的NetworkBoundResource Google Sample Project

    abstract class CachedResource<EntityType, ResponseType>
    @MainThread constructor(private val appExecutors: AppExecutors){

    // Called to save the result of the API response into the database
    @WorkerThread
    protected abstract fun saveCallResult(item: ResponseType)

    // Called with the data in the database to decide whether it should be
    // fetched from the network.
    @MainThread
    protected abstract fun shouldFetch(data: EntityType?): Boolean

    // Called to get the cached data from the database
    @MainThread
    protected abstract fun loadFromDb(): LiveData<EntityType>

    // Called to create the API call.
    @MainThread
    protected abstract fun createCall(): LiveData<Response<ResponseType>>

    @WorkerThread
    protected open fun processResponse(response: Response<ResponseType>) = response.data

    // Called when the fetch fails. The child class may want to reset components
    // like rate limiter.
    protected open fun onFetchFailed() {}

    // returns a LiveData that represents the resource, implemented
    // in the base class.
    fun asLiveData() = result as LiveData<Resource<ResponseType>>

    // IMPLEMENTATION

    private val result = MediatorLiveData<Resource<EntityType>>()

    init {
        result.value = Resource.loading(null)

        @Suppress("LeakingThis")
        val dbSource = loadFromDb()
        result.addSource(dbSource) { data ->
            result.removeSource(dbSource)

            // data loaded from database
            if (shouldFetch(data)) {
                fetchFromNetwork(dbSource)
            } else {
                result.addSource(dbSource) { newData ->
                    setValue(Resource.success(newData))
                }
            }
        }
    }

    @MainThread
    private fun setValue(newValue: Resource<EntityType>) {
        if (result.value != newValue) {
            result.value = newValue
        }
    }

    private fun fetchFromNetwork(dbSource: LiveData<EntityType>) {

        val apiSource = createCall()
        // we re-attach dbSource as a new source, it will dispatch its latest value quickly
        result.addSource(dbSource) { newData ->
            setValue(Resource.loading(newData))
        }
        result.addSource(apiSource) { response ->
            result.removeSource(apiSource)
            result.removeSource(dbSource)

            when(response?.status) {
                Response.Status.SUCCESS -> {
                    appExecutors.diskIO().execute {
                        val data = processResponse(response)
                        if (data != null) {
                            saveCallResult(data)
                            appExecutors.mainThread().execute {
                                // we specially request a new live data,
                                // otherwise we will get immediately last cached value,
                                // which may not be updated with latest results received from network.
                                result.addSource(loadFromDb()) { newData ->
                                    setValue(Resource.success(newData))
                                }
                            }
                        }
                    }
                }
                Response.Status.EMPTY -> {
                    appExecutors.mainThread().execute {
                        // reload from disk whatever we had
                        result.addSource(loadFromDb()) { newData ->
                            setValue(Resource.success(newData))
                        }
                    }
                }
                Response.Status.ERROR -> {
                    onFetchFailed()
                    result.addSource(dbSource) { newData ->
                        setValue(Resource.error(response.error, newData))
                    }
                }
            }
        }
    }
}

我已经测试了此架构,并且可以正确地从API加载数据,然后实时数据和视图模型也可以工作。也保存数据Room Database也可以正常工作。我已经从应用程序数据库目录下载了.db文件,其中包含正确的记录。

我不知道为什么当有新数据被提取并保存到Room时,LiveData observable为什么不通知我的观察者,在Resource.loading()情况下,它也不会返回缓存的数据。

感谢您的帮助

在调试器中,此代码仅用空数据值调用一次,从未发出任何实际用户,即使该代码在房间数据库中也是如此。 enter image description here

0 个答案:

没有答案