我对 RxJava 有疑问。
我有一个 getAll()
方法,它返回一个列表。它提供来自 Room 数据库的数据。
@Query("SELECT * from decks ORDER BY id ASC")
fun getAll(): Flowable<List<DeckEntity>>
DeckEntity
有一个 id
和一个 name
字段。
我创建了另一个名为 PrepareItem
的类,因为我想用更多参数将其装箱。 (它将是一个 Adapter
模型)检查它:
data class PrepareItem (
var deckEntity: DeckEntity,
var countsOfCards: Int
)
因此,我想调用 getAll()
并将其映射到 PrepareItem。它还在工作。
deckRepository.getAll()
.map {
it.map {
PrepareItem(it,0)
}
}
但是,countsOfCards 等于 0
。我想进行另一个存储库调用,以获取值并进行设置。重要的!每个值都需要对存储库进行一次调用。因此,如果我有 5 个项目,那么我需要等到另一个调用完成 5 个。
我试过了,但我很困惑。 (代码已更新)
fun mapper(item: DeckEntity) : Single<PrepareItem> {
return cardRepository.getDueDatedCardsFromDeck(deckId = item.id!! /*TODO !!*/)
.map {
PrepareItem(item, it.size)
}
}
val call = deckRepository.getAll()
.flatMapIterable { item->item }
.flatMapSingle {
mapper(it)
}.toList()
.toObservable()
...
onError
或 onComplete
从未调用过。为什么?
有人知道怎么做吗?我希望它远离存储库。 谢谢!
更新:
解决方案:
创建一个新类
class DeckWithCards {
@Embedded
lateinit var deckEntity: DeckEntity
@Relation(
entity = CardEntity::class,
entityColumn = "deckId",
parentColumn = "id")
lateinit var cards: List<CardEntity>
}
为DeckDao
添加新的乐趣
@Query("SELECT * from decks ORDER BY id ASC")
fun getAllWithCards(): Flowable<List<DeckWithCards>>
这就是它的全部作品!谢谢你的回答。这对我帮助很大!
最佳答案
你应该考虑把硬东西扔到数据库上。
考虑以下实体:
CardEntity
包含有关特定卡的信息。它还确保在单副牌中可以存在一张给定名称的牌。
@Entity(
tableName = "cards",
indices = {
@Index(value = { "name", "deck_id" }, unique = true)
}
)
public class CardEntity {
//region Column Definitions
@PrimaryKey
@ColumnInfo(name = "id")
private Long id;
@NonNull
@ColumnInfo(name = "name")
private String name = "";
@NonNull
@ColumnInfo(name = "deck_id")
private Long deckId = 0L;
//endregion
//region Getters and Setters
(...)
//endregion
}
DeckEntity
包含有关特定牌组的信息。
@Entity(tableName = "decks")
public class DeckEntity {
//region Column Definitions
@PrimaryKey
@ColumnInfo(name = "id")
private Long id;
@NonNull
@ColumnInfo(name = "name")
private String name = "";
//endregion
//region Getters and Setters
(...)
//endregion
}
DeckWithCardsView
是一个投影,它结合了 DeckEntity
、CardEntity
的集合和一些元数据(在本例中 - 许多卡片这副牌)。
public class DeckWithCardsView {
//region Deck
@Embedded
private DeckEntity deck;
public DeckEntity getDeck() {
return deck;
}
public void setDeck(DeckEntity deck) {
this.deck = deck;
}
//endregion
//region Cards
@Relation(
entity = CardEntity.class,
entityColumn = "deck_id",
parentColumn = "id")
private List<CardEntity> cards = new ArrayList<>();
public List<CardEntity> getCards() {
return cards;
}
public void setCards(List<CardEntity> cards) {
this.cards = cards;
}
//endregion
//region Cards count
private Integer count = 0;
public Integer getCount() {
return count;
}
public void setCount(Integer count) {
this.count = count;
}
//endregion
}
一个完整的 Dao
代码可以是这样的:
@Dao
public abstract class DeckDao {
//region Deck
@Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract void insertDeck(DeckEntity entity);
@Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract void insertDecks(List<DeckEntity> entities);
//endregion
//region Card
@Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract void insertCard(CardEntity entity);
@Insert(onConflict = OnConflictStrategy.IGNORE)
public abstract void insertCards(List<CardEntity> cards);
//endregion
//region Deck with Cards
@Transaction
@Query(
"SELECT D.id as id, D.name as name, count(C.id) as count "
+ "FROM decks D "
+ "INNER JOIN cards C "
+ "WHERE C.deck_id = D.id "
+ "GROUP BY deck_id")
public abstract Flowable<List<DeckWithCardsView>> getAll();
//endregion
}
它实现了以下功能:
- 将单副牌添加到数据库中,
- 将一组牌组添加到数据库中,
- 将单张卡片添加到数据库,
- 将卡片集合添加到数据库中,
- 从数据库中检索上述投影的列表。
最重要的部分在Sqlite查询代码中。逐行:
SELECT D.id as id, D.name as name, count(C.id) as count
它定义了我们期望查询将从数据库中返回值的确切列。特别是:
D.id as id
- 它将从别名为D
(甲板)的列返回id
,D.name as name
- 它将从别名为D
(甲板)的列返回name
,count(C.id) as count
- 它将返回从别名为C
(卡片)的列中检索到的一些 ID。
FROM decks D INNER JOIN cards C WHERE C.deck_id = D.id
它定义了我们想要从别名为 D
的表 decks
和别名为 C
的 cards
中检索值并定义 CardEntity
的 deck_id
与 DeckEntity
的 id
列的关系。
GROUP BY deck_id
多亏了分组,我们可以轻松地执行 count(C.id)
来检索每副牌返回的卡片数量。
由于我们还在 DeckWithCardsView
中使用了 @Embedded
注释 - 相应的(别名)列将与嵌入的 DeckEntity
牌组中的正确字段匹配字段。
数据库管理器
准备好这些实体、投影和 dao - 数据库管理器可以简单地实现为:
public Completable saveAllDecks(@Nullable List<DeckEntity> decks) {
return Completable.fromAction(
() -> database
.deckDao()
.insertDecks(decks)
).subscribeOn(Schedulers.io());
}
public Completable saveAllCards(@Nullable List<CardEntity> cards) {
return Completable.fromAction(
() -> database
.deckDao()
.insertCards(cards)
).subscribeOn(Schedulers.io());
}
public Flowable<List<DeckWithCardsView>> getDecksWithCards() {
return database
.deckDao()
.getAll()
.subscribeOn(Schedulers.io());
}
示例数据:
我还准备了一个示例代码,用于创建五副完整的牌组(每副牌中的所有花色和等级)。
private static final Long[] DECK_IDS = {
1L, 2L, 3L, 4L, 5L
};
private static final String[] DECK_NAMES = {
"Deck 1", "Deck 2", "Deck 3", "Deck 4", "Deck 5"
};
Completable prepareDecks() {
final Observable<Long> ids = Observable.fromArray(DECK_IDS);
final Observable<String> names = Observable.fromArray(DECK_NAMES);
final List<DeckEntity> decks = ids.flatMap(id -> names.map(name -> {
final DeckEntity entity = new DeckEntity();
entity.setId(id);
entity.setName(name);
return entity;
})).toList().blockingGet();
return cinemaManager.saveDecks(decks);
}
它创建一个 id 和 deck 名称的列表,并创建这些表的产品。平面映射此类产品会产生包含五个甲板实体的完整列表。
private static final String[] CARD_SUITS = {
"diamonds", "hearts", "spades", "clubs"
};
private static final String[] CARD_RANKS = {
"Two of ",
"Three of ",
"Four of ",
"Five of ",
"Six of ",
"Seven of",
"Eight of ",
"Nine of ",
"Ten of ",
"Jack of ",
"Queen of ",
"King of ",
"Ace of "
};
Completable prepareCards() {
final Observable<Long> decks = Observable.fromArray(DECK_IDS);
final Observable<String> suits = Observable.fromArray(CARD_SUITS);
final Observable<String> ranks = Observable.fromArray(CARD_RANKS);
final List<CardEntity> cards = decks.flatMap(deck -> suits.flatMap(suit -> ranks.map(rank -> {
final CardEntity entity = new CardEntity();
entity.setName(String.format("%s %s", rank, suit));
entity.setDeckId(deck);
return entity;
}))).toList().blockingGet();
return cinemaManager.saveCards(cards);
}
使用类似的方法,我为之前准备好的牌组创建了一个完整的卡片集合。
最后的润色:
艰苦的工作已经完成。剩下的简单部分:
private Completable prepareData() {
return prepareDecks().andThen(prepareCards());
}
void observeDecksWithCards() {
disposables.add(
prepareData()
.andThen(deckManager.getDecksWithCards())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
this::handleObserveDecksWithCardsSuccess,
this::handleObserveDecksWithCardsError));
}
private void handleObserveDecksWithCardsSuccess(@NonNull List<DeckWithCardsView> decks) {
Timber.v("Received number of decks: %d", decks.size());
}
private void handleObserveDecksWithCardsError(@NonNull Throwable throwable) {
Timber.e(throwable.getLocalizedMessage());
}
结果:
结果我们有两个表。对于甲板和卡片。订阅 getDecksWithCards
可以访问一组牌组。具有完整的卡片集合和卡片数量(由数据库计算)。
Remember - since the relation to table with cards has been made, you do not need to use the
count
sqlite method. You can just usegetCards().getSize()
inDeckWithCardsView
projection. This will simplify the code significantly.
关于android - RxJava 调用每个列表项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51499934/