Android 安卓机房:离线缓存不';行不通

Android 安卓机房:离线缓存不';行不通,android,kotlin,repository,android-room,Android,Kotlin,Repository,Android Room,我遵循了尝试在我的应用程序中实现在线缓存的过程。虽然一切看起来都很相似,但当我打开飞行模式时,数据并没有显示出来。这是我的密码: 存储库 class WeatherRepository(private val database: WeatherDatabase) { var weather: LiveData<CurrentWeather> = Transformations.map(database.weatherDao.getWeather()){ it

我遵循了尝试在我的应用程序中实现在线缓存的过程。虽然一切看起来都很相似,但当我打开飞行模式时,数据并没有显示出来。这是我的密码:

存储库

class WeatherRepository(private val database: WeatherDatabase) {

    var weather: LiveData<CurrentWeather> = Transformations.map(database.weatherDao.getWeather()){
        it.asDomainModel()
    }

    suspend fun refreshWeather(city: String){
        withContext(Dispatchers.IO){
            val weather = WeatherNetwork.service.getCurrentWeather("London", "metric")
            database.weatherDao.insert(weather.asDatabaseModel())
            Log.i("INSERTING: ", weather.asDatabaseModel().toString())
        }
    }

    //I've created this function only for debugging
    fun getDataFromDB(){
        weather = Transformations.map(database.weatherDao.getWeather()){
            it.asDomainModel()
        }
        val data = database.weatherDao.getWeather()
        Log.i("DB VALUES: ", data.value.toString())
        Log.i("VAR WEATHER IN REPO: ", weather.value.toString())
    }
}
const val WEATHER_ID = 0

@Entity(tableName = "current_weather")
data class DatabaseWeather constructor(
    val name: String,
    val lon: Double,
    val lat: Double,
    val description: String,
    val icon: String,
    val temp: Double,
    val tempFeelsLike: Double,
    val humidity: Int,
    val pressure: Int
){
    @PrimaryKey(autoGenerate = false)
    var id: Int = WEATHER_ID
}

fun DatabaseWeather.asDomainModel(): CurrentWeather{
    return CurrentWeather(
        name = this.name,
        lon = this.lon,
        lat = this.lat,
        description = this.description,
        icon = this.icon,
        temp = this.temp,
        tempFeelsLike = this.tempFeelsLike,
        humidity = this.humidity,
        pressure = this.pressure
    )
}
网络

const val BASE_URL = "https://api.openweathermap.org/data/2.5/"
const val API_KEY = "xxx"

private val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .build()

interface WeatherApiService {
    @GET("weather")
    suspend fun getCurrentWeather(
        @Query("q") cityName: String,
        @Query("units") units: String
    ): WeatherResponse
}

object WeatherNetwork{
    private val requestInterceptor = Interceptor { chain ->
        val url = chain.request()
            .url()
            .newBuilder()
            .addQueryParameter("appid", API_KEY)
            .build()

        val request = chain.request()
            .newBuilder()
            .url(url)
            .build()

        return@Interceptor chain.proceed(request)
    }

    private val okHttpClient = OkHttpClient.Builder()
        .addInterceptor(requestInterceptor)
        .build()

    private val retrofit = Retrofit.Builder()
        .client(okHttpClient)
        .baseUrl(BASE_URL)
        .addConverterFactory(MoshiConverterFactory.create(moshi))
        .build()

    val service = retrofit.create(WeatherApiService::class.java)
}
视图模型

enum class WeatherApiStatus { LOADING, ERROR, DONE }

class CurrentWeatherViewModel(application: Application) : AndroidViewModel(application) {

    val place = MutableLiveData<String>()

    private val _status = MutableLiveData<WeatherApiStatus>()

    val status: LiveData<WeatherApiStatus>
        get() = _status

    private val weatherRepository = WeatherRepository(getDatabase(application))

    val weather = weatherRepository.weather

    init {
        refreshDataFromRepository()
    }

    fun refreshDataFromRepository(){
        viewModelScope.launch {
            try{
                weatherRepository.refreshWeather(place.value.toString())
                _status.value = WeatherApiStatus.DONE
            } catch (e: Exception){
                Log.i("FAILURE: ", e.printStackTrace().toString())
                _status.value = WeatherApiStatus.ERROR
            }
        }
    }

    fun checkDB(){
        weatherRepository.getDataFromDB()
    }
}
enum类状态{正在加载,错误,完成}
类CurrentWeatherViewModel(应用程序:应用程序):AndroidViewModel(应用程序){
val place=MutableLiveData()
private val_status=MutableLiveData()
val状态:LiveData
获取()
私有val weatherRepository=weatherRepository(getDatabase(应用程序))
val weather=weatherRepository.weather
初始化{
refreshDataFromRepository()
}
fun refreshDataFromRepository(){
viewModelScope.launch{
试一试{
weatherRepository.refreshWeather(place.value.toString())
_status.value=WeatherApiStatus.DONE
}捕获(e:例外){
Log.i(“失败:”,例如printStackTrace().toString())
_status.value=WeatherApiStatus.ERROR
}
}
}
fun checkDB(){
weatherRepository.getDataFromDB()文件
}
}
当我不调用WeatherRepository中的refreshWeather方法(数据是从db下载的)时,它正在工作。
我不知道为什么,但是日志“DB VALUES”和“VAR WEATHER IN REPO”总是显示为空(“插入”正确显示数据)

当您尝试记录从房间DB检索到的值时遇到的问题,它总是显示为空,这是因为数据库检索过程始终是异步进行的,并且在尝试记录数据时数据尚未准备就绪

正如您所看到的,DAO的
getWeather()
方法返回一个
LiveData
,您在请求后立即尝试记录它的值,没有给足够的时间从磁盘获取它,因此导致仍然是
null

由于此类操作的IO性质(与代码和变量所在的CPU和RAM内存的速度相比,磁盘访问速度非常慢),您应该观察此
LiveData
,以便在成功从数据库检索到值时收到通知

仅用于调试目的(此代码会导致许多问题,例如性能和内存问题,因为它会一直监听
LiveData
,建议使用
observe
方法传递
LifecycleOwner
,而不是一直观察),您可以这样做:

    fun getDataFromDB(){
        weather = Transformations.map(database.weatherDao.getWeather()){
            it.asDomainModel()
        }
        val data = database.weatherDao.getWeather()

        data.observeForever { value ->
            Log.i("DB VALUES: ", value.toString())
        }
        weather.observeForever { value ->
            Log.i("VAR WEATHER IN REPO: ", value.toString())
        }
    }
另一种实现方法是将DAO函数的返回类型更改为直接返回
DatabaseWeather
,使其成为
suspend fun
,如下所示:

suspend fun getWeather(): DatabaseWeather
但是,您需要更改一组其他代码,只需“等待”获取值并在记录之前准备就绪(就像在“插入”情况下,由于其中的“挂起”函数,代码在协同路由上等待
WeatherNetwork
调用)

suspend fun getWeather(): DatabaseWeather