首页 > 解决方案 > 使用内部对象进行 RxJava 过滤

问题描述

首先,我必须说我是 RxJava 的初学者。

数据类:

    @Entity(tableName = "google_book")
    data class GoogleBook (
        @PrimaryKey(autoGenerate = true) val id: Int=0,
        val items: ArrayList<VolumeInfo>)
    
    data class VolumeInfo(val volumeInfo: BookInfo){
        data class BookInfo(val title: String, val publisher: String, val description: String, val imageLinks: ImageLinks?)
        data class ImageLinks(val smallThumbnail: String?)
    }

帮助我将数据保存到数据库的功能:

        fun searchBooks(query: String) {
            searchJob?.cancel()
            searchJob = viewModelScope.launch {
                val text = query.trim()
                if (text.isNotEmpty()) {
                    bookRepository.getBooksFromApi(query)
                        .map { t ->
                            t.items.map {
                                it.volumeInfo.imageLinks?.smallThumbnail?.filter { x -> x != null }
                            }
                            t
                        }
                        .subscribeOn(Schedulers.io())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe { x ->
                            x?.let { googleBook ->
                                searchJob?.cancel()
                                searchJob = viewModelScope.launch {
                                    bookRepository.deleteGoogleBook()
                                    bookRepository.insertGoogleBook(googleBook)
                                }
                            } ?: kotlin.run {
                                Log.d(TAG, "observeTasks: Error")
                            }
                        }
                }
            }
        }

正如所见,我想通过图像参数过滤 GoogleBook 对象中的列表,但它不起作用。我无法为数据类 ImageLinks 添加过滤,所以我不知道如何使它正确

我主要问的是这部分:

                     .map { t ->
                            t.items.map {
                                it.volumeInfo.imageLinks?.smallThumbnail?.filter { x -> x != null }
                            }
                            t
                        }

谢谢阅读

标签: androidkotlinrx-java

解决方案


欢迎来到 RxJava,你会喜欢它的。

据我所知,您的过滤问题仅依赖于这里:

.map { t ->
    t.items.map {
        it.volumeInfo.imageLinks?.smallThumbnail?.filter { x -> x != null })
    } // this returns you a new list filtered list here, but does not modify the original one
    t // but you return the same data object here, it is not modified at all
}
// also consider naming it bookInfo if it is actually a bookInfo

您应该做的是使用过滤后的元素制作对象的副本,如下所示:

fun filterGoogleBookBySmallThumbNail(googleBook: GoogleBook): GoogleBook {
    val filteredItems = googleBook.items.filter { it.volumeInfo.imageLinks?.smallThumbnail == null }
    return googleBook.copy(items = ArrayList(filteredItems)) // now a new googleBook item is created with the filtered elements
}

 // snippet to adjust then
 bookRepository.getBooksFromApi(query)
     .map { googleBook -> filterGoogleBookBySmallThumbNail(googleBook) }
     //...

我有一些额外的注释/建议:

  1. 我没有看到您实际上处理了 Observable 的订阅。

bookRepository.getBooksFromApi(query)如果此行返回一个 Observable,即使您取消作业,您仍将观察该 Observable。如果它返回一个 Single 那么你很幸运,因为在一个元素之后它就被处理掉了。要正确处理,取消时你必须做这样的事情(我仍然会推荐其他两个,只是想注意不处理):

searchJob = viewModelScope.launch {
    val text = query.trim()
    if (text.isNotEmpty()) {
        val disposable = bookRepository.getBooksFromApi(query)
            //...
            .subscribe { x ->
            //...
            }
        try {
            awaitCancellation() // this actually suspends the coroutine until it is cancelled
        } catch (cancellableException: CancellationException) {
            disposable.dispose() // this disposes the observable subscription
// that way the coroutine stays alive as long as it's not cancelled, and at that point it actually cleans up the Rx Subscription
        }
  1. 您开始一项新的协程工作只是为了执行操作似乎很浪费

如果你想采用 Rx 方式,你可以使 bookRepository.deleteGoogleBook()and bookRepository.insertGoogleBook(googleBook)Completable,并将 observable 设置为:

bookRepository.getBooksFromApi(query)
//..
.flatMap {
    bookRepository.deleteGoogleBook().andThen(bookRepository.insertGoogleBook(it)).andThen(Observable.just(it))
}
//..subscribeOn
.subscribe()
  1. 您以这种方式混合协程和 RX 似乎很奇怪

如果您不想使用完整的 Rx,您可以考虑将 Observable 转换为 kotlin 协程流,这样会更容易处理协程取消和调用挂起函数。

我希望它有帮助


推荐阅读