簡體   English   中英

使用Retrofit和Realm在RxJava中糾正流程

[英]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));

這是我的問題:

  1. 它是鏈接事件的正確方法,如上例所示,還是應該以不同的方式管理它們?

  2. 可以在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM