android - RxJava 调用每个列表项

标签 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 等于 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>>

这就是它的全部作品!谢谢你的回答。这对我帮助很大!

最佳答案

你应该考虑把硬东西扔到数据库上。

考虑以下实体:

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

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 和别名为 Ccards 中检索值并定义 CardEntitydeck_idDeckEntityid 列的关系。

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 可以访问一组牌组。具有完整的卡片集合和卡片数量(由数据库计算)。

enter image description here

Remember - since the relation to table with cards has been made, you do not need to use the count sqlite method. You can just use getCards().getSize() in DeckWithCardsView projection. This will simplify the code significantly.

关于android - RxJava 调用每个列表项,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51499934/

相关文章:

android - 杀死 Activity 的所有实例

android - listview onitemclicklistener 不总是响应

arrays - 将标题与String中的正文分开,或如何通过2 “\n”进行解析? Kotlin

android - 单一导致主线程上的网络或错误线程异常的查看根

android - 如何在 android 中处理 rxjava 链中的错误?

android - 在 Android App 中信任自签名证书

android - ERROR : Failed to resolve: com. android.support.constraint :constraintlayout:1. 1.3 在项目结构对话框中显示受影响的模块:app

android - 如何在 RxJava 流中间有条件地添加异步操作?

kotlin - 理解初始化为扩展函数的参数

java - 一个 Observable 对另一个 RxJava2 的依赖