6

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;
                });
    }
}

そして、ここRealmResults<Shop>に a の中に入る呼び出しがありFragmentます。

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. Realmメソッドでインスタンスを使用してgetShops()そこで i を閉じても問題ありませんか、それとも引数として渡してから何らかの方法で管理する方がよいでしょうか? ただし、このアイデアは、スレッドとRealm.close()常に適切なタイミングで呼び出すには少し問題があるようです。

4

3 に答える 3

11

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 が通知することに依存することもできます (ネットワークを DB アクセスから分離するため、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
            });
}
于 2016-06-28T07:19:21.233 に答える