RxJava会为每个列表项打电话

时间:2018-07-24 13:30:30

标签: android kotlin rx-kotlin2

我对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 is equal 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()
                ...

从未调用过onErroronComplete。为什么?

任何人都知道如何做到这一点?我希望它远离存储库。 谢谢!

更新:

解决方案:

创建一个新的班级

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>>

就可以了!谢谢你的回答。这对我有很大帮助!

1 个答案:

答案 0 :(得分:1)

您应该考虑将硬东西扔到数据库上。

请考虑以下实体:

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是一个投影,它将DeckEntityCardEntity的集合和一些元数据(在这种情况下-该卡片组中的许多纸牌)组合在一起。

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查询代码中。一行一行:

  

选择D.id作为ID,D.name作为名称,count(C.id)作为count

它定义了我们期望查询从哪些确切列中返回数据库的值。特别是:

  • D.id as id-它将从别名为id(甲板)的列中返回D
  • D.name as name-它将从别名为name(甲板)的列中返回D
  • count(C.id) as count-它会返回从别名为C(卡)的列中检索到的许多ID。
  

从甲板D内联接卡C在哪里C.deck_id = D.id

它定义了我们想要从别名为decks的表D和别名为cards的{​​{1}}的表中检索值,并定义了C上的关系CardEntitydeck_id的{​​{1}}列中。

  

GROUP BY deck_id

由于进行分组,我们可以轻松地DeckEntity来检索为每个卡组返回的卡数量。

由于我们还在id中使用了count(C.id)注释-对应的(别名)列将与嵌入的@Embedded平台字段中的正确字段匹配。

数据库管理器

已经准备好了这样的实体,投影和dao-数据库管理器可以很简单地实现:

DeckWithCardsView

示例数据:

我还准备了一个示例代码,该代码创建了五个完整的牌组(每个牌组中所有西装和等级)。

DeckEntity

它创建ID和牌组名称的列表,并创建此类表的产品。平面映射此类产品会产生五个甲板实体的完整列表。

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);
}

结果:

因此,我们有两个表。用于卡片组和卡片。订阅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); } 可以访问一组牌组。具有完整的卡片收集和卡片数目(由数据库计算)。

enter image description here

  

请记住-由于已完成与带卡牌桌的关系,因此不需要使用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()); } sqlite方法。您可以只在getDecksWithCards投影中使用count。这将大大简化代码。