[英]Correct flow in RxJava with Retrofit and Realm
我正在使用RxJava和Retrofit的組合實現網絡API,我使用Realm作為我的數據庫。 我得到了相當多的工作,但我想知道它是否是正確的方法和事件的流程。 所以,這是RetrofitApiManager
。
public class RetrofitApiManager {
private static final String BASE_URL = "***";
private final ShopApi shopApi;
public RetrofitApiManager(OkHttpClient okHttpClient) {
// GSON INITIALIZATION
Retrofit retrofit = new Retrofit.Builder()
.client(okHttpClient)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(BASE_URL)
.build();
shopApi = retrofit.create(ShopApi.class);
}
public Observable<RealmResults<Shop>> getShops() {
return shopApi.getShops()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(response -> {
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(realm1 ->
realm1.copyToRealmOrUpdate(response.shops));
realm.close();
})
.flatMap(response -> {
Realm realm = Realm.getDefaultInstance();
Observable<RealmResults<Shop>> results = realm.where(Shop.class)
.findAllAsync()
.asObservable()
.filter(RealmResults::isLoaded);
realm.close();
return results;
});
}
}
這是調用Fragment
RealmResults<Shop>
的調用。
realm.where(Shop.class)
.findAllAsync()
.asObservable()
.filter(RealmResults::isLoaded)
.first()
.flatMap(shops ->
shops.isEmpty() ? retrofitApiManager.getShops() : Observable.just(shops))
.subscribe(
shops -> initRecyclerView(),
throwable -> processError(throwable));
這是我的問題:
它是鏈接事件的正確方法,如上例所示,還是應該以不同的方式管理它們?
可以在getShops()
方法中使用Realm
實例並在getShops()
關閉我或者將它作為參數傳遞然后以某種方式管理它會更好嗎? 雖然,這個想法似乎對線程有點問題,並且總是在正確的時間調用Realm.close()
。
1)我會嘗試在后台線程上盡可能多地做,現在你在UI線程上做了很多工作。
2)
public Observable<RealmResults<Shop>> getShops() {
return shopApi.getShops()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(response -> {
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction(realm1 ->
realm1.insertOrUpdate(response.shops));
} // auto-close
})
.flatMap(response -> {
try(Realm realm = Realm.getDefaultInstance()) {
Observable<RealmResults<Shop>> results = realm.where(Shop.class)
.findAllAsync()
.asObservable()
.filter(RealmResults::isLoaded);
} // auto-close
return results;
});
}
所有Realm數據都是延遲加載的,所以它只在Realm實例打開時才可用,因此在檢索它之后關閉它很有可能無法正常工作。 在你的情況下,雖然你是主線程上的平面映射,所以很可能已經有一個打開的實例。
如果您願意,可以使用copyFromRealm()
來獲取可以跨線程移動的非托管數據,並且不再連接到Realm,但是它們也將失去其實時更新功能並占用更多內存。
它可能會這樣做:
public Observable<RealmResults<Shop>> getShops() {
return shopApi.getShops()
.subscribeOn(Schedulers.io())
.doOnNext(response -> {
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction(realm1 ->
realm1.copyToRealmOrUpdate(response.shops));
} // auto-close
})
.observeOn(AndroidSchedulers.mainThread())
.flatMap(response -> {
Observable<RealmResults<Shop>> results = realm.where(Shop.class)
.findAllAsync()
.asObservable()
.filter(RealmResults::isLoaded);
return results;
});
或者,您可以將網絡請求視為副作用,並且只是依賴於Realm在有更改時通知您(更好地接近IMO,因為您將網絡與數據庫訪問分開,例如存儲庫模式的內容)
public Observable<RealmResults<Shop>> getShops() {
// Realm will automatically notify this observable whenever data is saved from the network
return realm.where(Shop.class).findAllAsync().asObservable()
.filter(RealmResults::isLoaded)
.doOnNext(results -> {
if (results.size() == 0) {
loadShopsFromNetwork();
}
});
}
private void loadShopsFromNetwork() {
shopApi.getShops()
.subscribeOn(Schedulers.io())
.subscribe(response -> {
try(Realm realm = Realm.getDefaultInstance()) {
realm.executeTransaction(r -> r.insertOrUpdate(response.shops));
} // auto-close
});
}
Christian Melchior在他的回答中提到的是完全合理的,並且應該解決你手頭的問題,但是這種方法可能會引入其他問題。
在良好的體系結構中,所有主要模塊(或庫)都應與其余代碼隔離開來。 由於Realm,RealmObject或RealmResult不能跨線程傳遞,因此將Realm&Realm相關操作與其余代碼隔離更為重要。
對於每個jsonModel類,您應該有一個realmModel類和一個DAO(數據訪問對象)。 這里的想法是除了DAO類之外,沒有一個類必須知道或訪問realmModel或Realm。 DAO類接受jsonModel,轉換為realmModel,執行讀/寫/編輯/刪除操作,對於讀取操作,DAO將realmModel轉換為jsonModel並隨之返回。
這樣就很容易維護Realm,避免所有Thread相關的問題,易於測試和調試。
這是一篇關於Realm最佳實踐的文章,它有一個很好的架構https://medium.com/@Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f
此外,還展示了一個示例項目,演示了Android上的Realm與MVP(模型視圖演示器),RxJava,Retrofit,Dagger,Annotations和Testing的集成。 https://github.com/viraj49/Realm_android-injection-rx-test
就我而言,我似乎已經為RealmRecyclerViewAdapter
定義了一個查詢:
recyclerView.setAdapter(new CatAdapter(getContext(),
realm.where(Cat.class).findAllSortedAsync(CatFields.RANK, Sort.ASCENDING)));
並且在滿足條件時使用RxJava定義Retrofit條件以下載更多內容:
Subscription downloadCats = Observable.create(new RecyclerViewScrollBottomOnSubscribe(recyclerView))
.filter(isScrollEvent -> isScrollEvent || realm.where(Cat.class).count() <= 0)
.switchMap(isScrollEvent -> catService.getCats().subscribeOn(Schedulers.io())) // RETROFIT
.retry()
.subscribe(catsBO -> {
try(Realm outRealm = Realm.getDefaultInstance()) {
outRealm.executeTransaction((realm) -> {
Cat defaultCat = new Cat();
long rank;
if(realm.where(Cat.class).count() > 0) {
rank = realm.where(Cat.class).max(Cat.Fields.RANK.getField()).longValue();
} else {
rank = 0;
}
for(CatBO catBO : catsBO.getCats()) {
defaultCat.setId(catBO.getId());
defaultCat.setRank(++rank);
defaultCat.setSourceUrl(catBO.getSourceUrl());
defaultCat.setUrl(catBO.getUrl());
realm.insertOrUpdate(defaultCat);
}
});
}
}, throwable -> {
Log.e(TAG, "An error occurred", throwable);
});
這是基於編輯文本輸入的搜索:
Subscription filterDogs = RxTextView.textChanges(editText)
.switchMap((charSequence) ->
realm.where(Dog.class)
.contains(DogFields.NAME, charSequence.toString())
.findAllAsyncSorted(DogFields.NAME, Sort.ASCENDING)
.asObservable())
.filter(RealmResults::isLoaded)
.subscribe(dogs -> realmRecyclerAdapter.updateData(dogs));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.