简体   繁体   English

RXJava 2-使用领域和改造来获取数据

[英]Rxjava 2 - fetch data with realm and retrofit

Task: I want to fetch local data first. 任务:我想先获取本地数据。 If returned data is null I want to call API service. 如果返回的数据为空,我想调用API服务。

Presenter : 主讲人

private fun getData() {
    val disposable = dataRepository.getDataFromRepository(String: itemId)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ _ ->
                // I got my data
            }, {
                // error
            })
    compositeDisposable.add(disposable)
}

DataRepository : 资料储存库

fun getDataFromRepository(itemId: String): Single<Data> {
    val dataAsSingle: Single<Event>

    //fetch data locally
    dataAsSingle = getDataLocally(itemId)

    //if nothing was found locally, call api service to fetch data
    if (dataAsSingle == null) {
        getDataFromApi(itemId)
    }

    return dataAsSingle
}

private fun getDataLocally(itemId: String): Single<Data> {
    var data: Data? = null

    val realm = Realm.getDefaultInstance()
        data = realm.where(Data::class.java).equalTo(itemId)
    // some other logic..
    realm.close()

    return data?.let {
        Single.just(data)
    } ?: Single.error(NoSuchEventFoundException())
}

private fun getDataFromApi(itemId: String): Single<Data> {
    return dataService.getDataFromApiCall(itemId)
            .onErrorResumeNext(ErrorHandler(BaseErrorParser()))
            .map { apiResponse ->
                    //some logic blah blah blah...
                    return@map data
                } 
            }
}

Well, I think my approach is wrong, but I can't initally tell you why... This works okay, but it seems that it skips main points of RxJava. 好吧,我认为我的方法是错误的,但是我不能一开始就告诉您原因。。。这个方法还可以,但是似乎它跳过了RxJava的要点。 Maybe someone can explain how to solve this problem with correct pattern and approach using RxJava 2? 也许有人可以解释如何使用RxJava 2以正确的模式和方法解决此问题?

Updated 更新

DataRepository : 资料储存库

fun getDataFromRepository(itemId: String): Single<Data> {

    val localData: Observable<Data> = getDataLocally(itemId)
    val remoteData: Observable<Data> = getDataFromApi(itemId)
           .doOnNext(

            })

    return Observable
            .concat(localEvent, remoteEvent)
            .first()
}

private fun getDataLocally(itemId: String): Observable<Data> {
    var data: Data? = null

    val realm = Realm.getDefaultInstance()
        data = realm.where(Data::class.java).equalTo(itemId)
    // some other logic..
    realm.close()

    return Observable.just(data)
}

private fun getDataFromApi(itemId: String): Observable<Data> {
    return dataService.getDataFromApiCall(itemId)
            .map { apiResponse ->
                    //some logic blah blah blah...
                    return@map data
                } 
            }
}

You can use concat operator. 您可以使用concat运算符。 The important part is you should not return null value from your local source. 重要的部分是您不应从本地源返回空值。 You should return Empty Observable to get remote call. 您应该返回Empty Observable以获取远程呼叫。 Here's an example code: 这是一个示例代码:

final Observable<Page> localResult = mSearchLocalDataSource.search(query);
final Observable<Page> remoteResult = mSearchRemoteDataSource.search(query)
                .doOnNext(new Action1<Page>() {
                    @Override
                    public void call(Page page) {
                        if (page != null) {
                            mSearchLocalDataSource.save(query, page);
                            mResultCache.put(query, page);
                        }
                    }
                });

        return Observable.concat(localResult, remoteResult)
                .first()
                .map(new Func1<Page, Page>() {
                    @Override
                    public Page call(Page page) {
                        if (page == null) {
                            throw new NoSuchElementException("No result found!");
                        }
                        return page;
                    }
});

Actually, the proper way to do it with Realm would be to listen for changes in the Realm, and update the Realm from a background thread from the API if the Realm doesn't contain what you're looking for 实际上,使用Realm执行此操作的正确方法是侦听Realm中的更改,如果Realm不包含您要查找的内容,则从API的后台线程更新Realm

(with limitation of using this method from UI thread, in order to receive RealmResults to UI thread - this way you can use async transaction and listen for changes in Realm): (在从UI线程使用此方法的限制下,以便将RealmResults接收到UI线程-这样,您可以使用异步事务并监听Realm中的更改):

private fun getData() {
    val disposable = dataRepository.getDataFromRepository(String: itemId)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe({ _ ->
                // I got my data
            }, {
                // error
            })
    compositeDisposable.add(disposable)
}

Where: 哪里:

fun getDataFromRepository(itemId: String): Observable<Data> =
    Observable.create { emitter ->
        val realm = Realm.getDefaultInstance()
        val results = realm.where<Data>().equalTo("itemId", itemId).findAllAsync()
        val listener = RealmChangeListener { results ->
            if(!emitter.isDisposed()) {
                emitter.onNext(results)
            }
        }
        emitter.setDisposable(Disposable {
            results.removeChangeListener(listener)
            realm.close()
        })
        results.addChangeListener(listener)
    }.subscribeOn(AndroidSchedulers.mainThread())
    .doOnNext { results ->
        if(results.isEmpty()) {
            dataService.getDataFromApiCall(itemId)
                       // .toSingle() // ensure this completes automatically, but only if it's not a single already
                       .subscribeOn(Schedulers.io())
                       .subscribeWith(object: DisposableObserver<>() {
                           fun onNext(data: Data) {
                               Realm.getDefaultInstance().use { realm ->
                                  realm.executeTransaction { realm ->
                                      realm.insert(data)
                                   }
                               }
                           }

                           fun onError(throwable: Throwable) {
                               ... /* do something */ 
                           }
                      )
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM