将 Retrofit 数据插入 Room 并使用 ViewModel 显示的正确方法
Proper way to insert Retrofit data to Room and display it using ViewModel
从 Retrofit api 获取数据、将其插入 Room 数据库并在 Viewmodel 中显示来自 Room 的数据的正确(最佳实践)方法是什么?将数据从 Retrofit 插入 Room 的操作应该发生在存储库还是另一个 class 中? Room数据应该如何返回给viewmodel?
截至目前,我的代码使用 Retrofit <- repository <- ViewModel <- Fragment 获取数据
-- 使用 Hilt 进行 di。还对 Retrofit 和 Room 实体
使用相同的数据 class
如有任何建议或实施建议,我们将不胜感激
存储库:
class ItemRepository @Inject constructor(
private val api: ShopraApi
) {
suspend fun getItems() = api.getUserItemFeed()
}
Api:
interface ShopraApi {
companion object {
const val BASE_URL = "-"
}
@GET("getUserItemFeed.php?user_id=1")
suspend fun getUserItemFeed() : List<Item>
}
视图模型:
@HiltViewModel
class ItemViewModel @Inject constructor(
itemRepository: ItemRepository
) : ViewModel() {
private val itemsLiveData = MutableLiveData<List<Item>>()
val items: LiveData<List<Item>> = itemsLiveData
init {
viewModelScope.launch {
itemsLiveData.value = itemRepository.getItems()
}
}
}
实体:
@Entity(tableName = "item_table")
data class Item(
@PrimaryKey(autoGenerate = false)
@NonNull
val listing_id: Long,
val title: String,
val description: String
)
道:
@Dao
interface ItemDao {
@Query("SELECT * FROM item_table")
fun getItemsFromRoom(): LiveData<List<Item>>
@Insert(onConflict = IGNORE)
suspend fun insert(item: Item)
@Insert(onConflict = IGNORE)
suspend fun insertAllItems(itemRoomList: List<Item>)
}
物品数据库:
@Database(entities = [Item::class],version = 1)
abstract class ItemDatabase : RoomDatabase() {
abstract fun itemDao() : ItemDao
}
应用模块:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(ShopraApi.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideShopraApi(retrofit: Retrofit): ShopraApi {
return retrofit.create(ShopraApi::class.java)
}
@Provides
@Singleton
fun provideDatabase(app: Application) =
Room.databaseBuilder(app, ItemDatabase::class.java, "item_database")
.fallbackToDestructiveMigration()
.build()
@Provides
fun provideTaskDao(db: ItemDatabase) = db.itemDao()
}
编辑更新存储库:
class ItemRepository @Inject constructor(
private val api: ShopraApi,
private val itemDao: ItemDao
) {
fun loadItems(): LiveData<List<Item>> {
return liveData{
val getItems = api.getUserItemFeed()
Log.e("ItemRepository","The size of the item list is
${getItems.size}")
getItems.forEach {
item -> itemDao.insert(item)
}
val loadFromRoom = itemDao.getItemsFromRoom()
emitSource(loadFromRoom)
}
}
}
已更新 ItemViewModel:
class ItemViewModel @Inject constructor(
itemRepository: ItemRepository
) : ViewModel() {
val items: LiveData<List<Item>> = itemRepository.loadItems()
}
然后我在我的片段中调用这段代码
viewModel.items.observe(viewLifecycleOwner) { items ->
itemAdapter.submitList(items)
您必须在 Repository 中执行此操作,而 Repository 是“单一事实来源”,当您从网络获取数据并保存到其中时,这是您做出决定的地方localDB 和来自 localDB 的 load/expose 将成为 room persistence
您可以使用名为 Network Bound Resource 的实用程序 class 并且有很多网络绑定资源的实现,这个来自官方示例 google's sample of network bound resource,
这是通用类型抽象 class 用于此特定目的,但如果您仍想以自定义方式进行,这就是您需要做的。
1-> 进行网络调用并从网络获取数据,这些数据你不会暴露给 viewmodel
2-> 收到数据后插入 localDB。
3-> 将您的数据从 localDB 加载或公开到视图模型
代码示例:
class ItemRepo(
val dao: Dao,
val api: Api
) {
fun loadItems(): LiveData<List<Item>> {
return liveData {
// get from network
val getFromNetwork = api.getList()
// save into local
getFromNetwork.forEach{
item ->
dao.insertItem(item)
}
//load from local
val loadFromLocal = dao.getAllItem()
emitSource(loadFromLocal)
}
}
}
您将 Inject
视图模型中的存储库,视图将从那里观察数据,这样您只能从 localDB 获取数据。
注意:这只是一个示例,您可以根据您的用例进一步重复使用它,例如错误处理、网络处理,例如当网络不可用时或遇到错误时您应该做什么......诸如此类.
从 Retrofit api 获取数据、将其插入 Room 数据库并在 Viewmodel 中显示来自 Room 的数据的正确(最佳实践)方法是什么?将数据从 Retrofit 插入 Room 的操作应该发生在存储库还是另一个 class 中? Room数据应该如何返回给viewmodel?
截至目前,我的代码使用 Retrofit <- repository <- ViewModel <- Fragment 获取数据 -- 使用 Hilt 进行 di。还对 Retrofit 和 Room 实体
使用相同的数据 class如有任何建议或实施建议,我们将不胜感激
存储库:
class ItemRepository @Inject constructor(
private val api: ShopraApi
) {
suspend fun getItems() = api.getUserItemFeed()
}
Api:
interface ShopraApi {
companion object {
const val BASE_URL = "-"
}
@GET("getUserItemFeed.php?user_id=1")
suspend fun getUserItemFeed() : List<Item>
}
视图模型:
@HiltViewModel
class ItemViewModel @Inject constructor(
itemRepository: ItemRepository
) : ViewModel() {
private val itemsLiveData = MutableLiveData<List<Item>>()
val items: LiveData<List<Item>> = itemsLiveData
init {
viewModelScope.launch {
itemsLiveData.value = itemRepository.getItems()
}
}
}
实体:
@Entity(tableName = "item_table")
data class Item(
@PrimaryKey(autoGenerate = false)
@NonNull
val listing_id: Long,
val title: String,
val description: String
)
道:
@Dao
interface ItemDao {
@Query("SELECT * FROM item_table")
fun getItemsFromRoom(): LiveData<List<Item>>
@Insert(onConflict = IGNORE)
suspend fun insert(item: Item)
@Insert(onConflict = IGNORE)
suspend fun insertAllItems(itemRoomList: List<Item>)
}
物品数据库:
@Database(entities = [Item::class],version = 1)
abstract class ItemDatabase : RoomDatabase() {
abstract fun itemDao() : ItemDao
}
应用模块:
@Module
@InstallIn(SingletonComponent::class)
object AppModule {
@Provides
@Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl(ShopraApi.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
@Provides
@Singleton
fun provideShopraApi(retrofit: Retrofit): ShopraApi {
return retrofit.create(ShopraApi::class.java)
}
@Provides
@Singleton
fun provideDatabase(app: Application) =
Room.databaseBuilder(app, ItemDatabase::class.java, "item_database")
.fallbackToDestructiveMigration()
.build()
@Provides
fun provideTaskDao(db: ItemDatabase) = db.itemDao()
}
编辑更新存储库:
class ItemRepository @Inject constructor(
private val api: ShopraApi,
private val itemDao: ItemDao
) {
fun loadItems(): LiveData<List<Item>> {
return liveData{
val getItems = api.getUserItemFeed()
Log.e("ItemRepository","The size of the item list is
${getItems.size}")
getItems.forEach {
item -> itemDao.insert(item)
}
val loadFromRoom = itemDao.getItemsFromRoom()
emitSource(loadFromRoom)
}
}
}
已更新 ItemViewModel:
class ItemViewModel @Inject constructor(
itemRepository: ItemRepository
) : ViewModel() {
val items: LiveData<List<Item>> = itemRepository.loadItems()
}
然后我在我的片段中调用这段代码
viewModel.items.observe(viewLifecycleOwner) { items ->
itemAdapter.submitList(items)
您必须在 Repository 中执行此操作,而 Repository 是“单一事实来源”,当您从网络获取数据并保存到其中时,这是您做出决定的地方localDB 和来自 localDB 的 load/expose 将成为 room persistence
您可以使用名为 Network Bound Resource 的实用程序 class 并且有很多网络绑定资源的实现,这个来自官方示例 google's sample of network bound resource,
这是通用类型抽象 class 用于此特定目的,但如果您仍想以自定义方式进行,这就是您需要做的。
1-> 进行网络调用并从网络获取数据,这些数据你不会暴露给 viewmodel
2-> 收到数据后插入 localDB。
3-> 将您的数据从 localDB 加载或公开到视图模型
代码示例:
class ItemRepo(
val dao: Dao,
val api: Api
) {
fun loadItems(): LiveData<List<Item>> {
return liveData {
// get from network
val getFromNetwork = api.getList()
// save into local
getFromNetwork.forEach{
item ->
dao.insertItem(item)
}
//load from local
val loadFromLocal = dao.getAllItem()
emitSource(loadFromLocal)
}
}
}
您将 Inject
视图模型中的存储库,视图将从那里观察数据,这样您只能从 localDB 获取数据。
注意:这只是一个示例,您可以根据您的用例进一步重复使用它,例如错误处理、网络处理,例如当网络不可用时或遇到错误时您应该做什么......诸如此类.