I have the following Model for my API Response:
@Entity(tableName = TABLE_NAME)
class WeatherEntry {
@PrimaryKey(autoGenerate = true)
var wID: Long? = null
@SerializedName("dt")
@ColumnInfo(name = COLUMN_DATE)
var date: String = ""
@SerializedName("city")
@Embedded(prefix = "location_")
var location: Location? = null
@SerializedName("main")
@Embedded(prefix = "main_")
var main: Main? = null
@SerializedName("weather")
@TypeConverters(Converters::class)
@Embedded(prefix = "weather_")
var weather: ArrayList<Weather>? = null
@SerializedName("wind")
@Embedded(prefix = "wind_")
var wind: Wind? = null
}
Weather Repo
Fetches Data from Local or Remote Data Source, I set forceRemote
to true
, because otherwise there would be no data show in the first place.
class WeatherRepository @Inject constructor(@Local var localDataSource: WeatherDataSource, @Remote var remoteDataSource: WeatherDataSource) :
WeatherDataSource {
private var caches: MutableList<WeatherEntry> = mutableListOf()
override fun getWeatherEntries(location: String, forceRemote: Boolean): Flowable<MutableList<WeatherEntry>> {
if (forceRemote) {
return refreshData(location)
} else {
return if (caches.isNotEmpty()) {
// if cache is available, return it immediately
Flowable.just(caches)
} else {
// else return data from local storage
localDataSource.getWeatherEntries(location, false)
.take(1)
.flatMap(({ Flowable.fromIterable(it) }))
.doOnNext { question -> caches.add(question) }
.toList()
.toFlowable()
.filter({ list -> !list.isEmpty() })
.switchIfEmpty(refreshData(location)) // If local data is empty, fetch from remote source instead.
}
}
}
/**
* Fetches data from remote source.
* Save it into both local database and cache.
*
* @return the Flowable of newly fetched data.
*/
private fun refreshData(location: String): Flowable<MutableList<WeatherEntry>> {
return remoteDataSource.getWeatherEntries(location,true).doOnNext({
// Clear cache
caches.clear()
// Clear data in local storage
localDataSource.deleteAllWeatherEntries()
}).flatMap(({ Flowable.fromIterable(it) })).doOnNext({ entry ->
caches.add(entry)
localDataSource.insertWeatherEntry(entry)
}).toList().toFlowable()
}
Local Data Source
class WeatherLocalDataSource @Inject constructor(private var weatherDao: WeatherDao): WeatherDataSource {
override fun insertWeatherEntry(weatherEntry: WeatherEntry) {
return weatherDao.insert(weatherEntry)
}
...
}
Remote Data Source This one definitely works as I'm getting all information from the api.
class WeatherRemoteDataSource @Inject constructor(var weatherService: WeatherService) :
WeatherDataSource {
override fun getWeatherEntries(location: String, forceRemote: Boolean): Flowable<MutableList<WeatherEntry>> {
return weatherService.getForecast(
location,
"json",
"metric",
BuildConfig.OPEN_WEATHER_MAP_API_KEY
).map(WeatherForecastResponse::weatherEntries)
}
}
DAO
@Dao
interface WeatherDao {
...
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(weatherEntry: WeatherEntry)
}
Database
@Database(
entities = [(WeatherEntry::class)],
version = 1
)
abstract class WeatherDatabase : RoomDatabase() {
abstract fun weatherDao(): WeatherDao
}
All other fields work correctly, but wID
is always null. What is wrong with my implementation?
I already tried to change it's default value to 0
and change the type to Int but that's not working either.
Try making the id non-nullable:
@PrimaryKey(autoGenerate = true)
var wID: Long = 0
EDIT: I've found this in the sample code here. you can make your @Insert methods return the id of the inserted row object, so you could do this:
In your Dao:
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insert(weatherEntry: WeatherEntry) : Long
In your refresh data method:
private fun refreshData(location: String): Flowable<MutableList<WeatherEntry>> {
return remoteDataSource.getWeatherEntries(location,true).doOnNext({
// Clear cache
caches.clear()
// Clear data in local storage
localDataSource.deleteAllWeatherEntries()
}).flatMap(({ Flowable.fromIterable(it) })).doOnNext({ entry ->
val entryID = localDataSource.insertWeatherEntry(entry)
entry.wID = entryID
caches.add(entry)
}).toList().toFlowable()
}