Flusso corretto in RxJava con Retrofit e Realm

Sto implementando API di networking con la combinazione di RxJava e Retrofit e utilizzo Realm come database. Ho preso molto lavoro, ma mi chiedo se sia l'approccio corretto e il stream di events. Quindi, ecco il 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; }); } } 

Ecco la chiamata per get RealmResults<Shop> all'interno di un Fragment .

  • Imansible trovare CMake in studio android
  • Come risolvere android.os.DeadObjectException android X
  • Differenza tra preferenza e preferenza condivisa in Android
  • Possiamo istanziare una class astratta?
  • Variabile dell'ambiente in settings.gradle che non funziona con Android Studio
  • Come creare / scrivere il file nella root del dispositivo android?
  •  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)); 

    Ecco le mie domande:

    1. È un approccio corretto per lanciare events come nell'esempio precedente o dovrei gestirli in modo diverso?

    2. È bene utilizzare l'istanza Realm in metodo getShops() e chiudere lì o sarebbe meglio passarlo come un argomento e poi gestirlo in qualche modo? Anche se, questa idea sembra essere un po 'problematica con i thread e chiamando Realm.close() sempre al momento giusto.

  • Come posso creare un'applicazione Java utilizzando l'API di Monkeyrunner?
  • Creazione di una galleria di immagini utilizzando immagini disegnabili nella cartella res
  • Avviare un'attività con un parametro
  • Crash di Android, java.lang.IllegalArgumentException: il tipo di window non può essere modificato dopo che viene aggiunta la window
  • Il codice HTTP 400 restituito durante la comunicazione con GCM
  • Come get frammenti esistenti quando si utilizza FragmentPagerAdapter
  • 3 Solutions collect form web for “Flusso corretto in RxJava con Retrofit e Realm”

    1) Vorrei cercare di fare il più ansible sul thread di background, adesso stai facendo un sacco di lavori sul thread 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; }); } 

    Tutti i dati di Realm sono caricati pigri, quindi è disponibile solo quando l'istanza Realm è aperta, quindi chiudendola dopo averlo recuperato ha un'alta probabilità di non funzionare. Nel tuo caso sebbene tu sia la mapping piatta sul thread principale, quindi probabilmente c'è già un'istanza aperta.

    Se vuoi puoi usare copyFromRealm() per get dati non gestibili che possono essere spostati in thread e non sono più connessi a Realm, ma anche perdono le proprie funzionalità di aggiornamento dal vivo e prendono più memory.

    Probabilmente farebbe questo invece:

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

    In alternativa, è ansible trattare la richiesta di networking come effetto collaterale e dipenderà solo da Realm che ti informa quando ci sono modifiche (approccio migliore per l'IMO quando si separa la networking dall'accesso DB quale è ad esempio il model Repository)

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

    Nel mio caso, sembro avere definito una query per il RealmRecyclerViewAdapter come questo:

      recyclerView.setAdapter(new CatAdapter(getContext(), realm.where(Cat.class).findAllSortedAsync(CatFields.RANK, Sort.ASCENDING))); 

    E altrimenti definito una condizione per Retrofit con RxJava per scaricare più cose quando la condizione è soddisfatta:

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

    E questa è ad esempio una ricerca basata sull'input di un text di modifica:

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

    Quello che Christian Melchior ha menzionato nella sua risposta, ha un senso perfetto e deve risolvere il problema che stai a portta di mano, ma in fondo alla linea questo approccio può introdurre altre questioni.

    In una buona architettura, tutti i principali moduli (o librerie) dovrebbero essere isolati dal resto del codice. Poiché Realm, RealmObject o RealmResult non possono essere passati attraverso thread, è ancora più importnte fare realm & realm operazioni correlate isolate dal resto del codice.

    Per ciascuna class di jsonModel, si dovrebbe avere una class realmModel e un DAO (Data Access Object). L'idea qui è che, oltre alla class DAO, nessuna delle classi deve conoscere o accedere a realmModel o Realm. La class DAO prende jsonModel, converte in realmModel, elabora operazioni di lettura / scrittura / modifica / rimozione, per operazioni di lettura DAO converte realmModel in jsonModel e ritorna con esso.

    In questo modo è facile mantenere Realm, evitare tutti i problemi legati al Thread, facile da testare e eseguire il debug.

    Ecco un articolo sulle best practices di Realm con una buona architettura https://medium.com/@Viraj.Tank/realm-integration-in-android-best-practices-449919d25f2f

    Anche un progetto di esempio che dimostra l'integrazione del reame su Android con MVP (Visualizzatore di templates view), RxJava, Retrofit, Dagger, Annotazioni e test. https://github.com/viraj49/Realm_android-injection-rx-test

    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.