Realm: lavorare con Clean-Architecture e RxJava2

Un po 'di context, ho cercato di applicare alcune architetture pulite a uno dei miei progetti e ho problemi con l'implementazione del disco (Realm) del mio repository. Ho un Repository che tira alcuni dati da DataStore diversi a seconda di alcune condizioni (cache). Questa è la teoria, il problema è quando si mescola tutto con UseCases e RxJava2.

Prima ho l'elenco di oggetti di Realm e poi creo manualmente un Observable di esso. Ma l' subscribe (come previsto) viene eseguito su un thread diverso, così il reame finisce per arrestare … (secondo block di codice)

  • Come posso get la data in formato MM / DD / YY da Timestamp
  • Utilizzo di onConfigurationChanged in un frammento
  • Come eseguire un ciclo infinito in Android senza congelare l'interface utente?
  • come avviare un'attività secondaria nella stessa vista?
  • È ansible visualizzare la barra di avanzamento quando si scarica tramite Retrofit 2 Asynchronous?
  • Come utilizzare la visualizzazione del calendario personalizzato in Android e come impostare l'avviso per il promemory?
  • Questo è il codice utilizzato per creare Observables (da una class astratta DiskStoreBase ):

     Observable<List<T>> createListFrom(final List<T> list) { return Observable.create(new ObservableOnSubscribe<List<T>>() { @Override public void subscribe(ObservableEmitter<List<T>> emitter) throws Exception { if (list != null) { emitter.onNext(list); emitter.onComplete(); } else { emitter.onError(new ExceptionCacheNotFound()); } } }); } 

    Come posso occuparmi di questo scenario?

    Più codice di DiskStoreForZone :

     @Override public Observable<List<ResponseZone>> entityList() { Realm realm = Realm.getDefaultInstance(); List<ResponseZone> result = realm.where(ResponseZone.class).findAll(); return createListFrom(result); } 

    L'incidente esatto:

     E/REALM_JNI: jni: ThrowingException 8, Realm accessed from incorrect thread. E/REALM_JNI: Exception has been thrown: Realm accessed from incorrect thread. 

  • OpenCV con immagini di cattura di Android
  • Sincronizza le posizioni di scorrimento di ScrollView - android
  • Come utilizzare il metodo setDuration () in SnackBar (Libreria di supporto per il design di Android)
  • Fai carosello con ViewFlipper o ViewPager
  • java.text.ParseException: data irripetibile
  • Firmare un apk esistente utilizzando debug.keystore generato da eclipse
  • One Solution collect form web for “Realm: lavorare con Clean-Architecture e RxJava2”

    Non funziona perché nonostante l'utilizzo di Rx, il tuo livello di dati non è reattivo.

    Il reame per sua natura è un'origine di dati retriggers , ei suoi oggetti gestiti per natura sono anche mutevoli (aggiornati in luogo da Realm) e filettati (possono essere acceduti solo sullo stesso thread in cui è stato aperto il Reame).

    Per poter utilizzare il codice, dovrai copiare i dati dal reame.

     @Override public Single<List<ResponseZone>> entityList() { return Single.fromCallable(() -> { try(Realm realm = Realm.getDefaultInstance()) { return realm.copyFromRealm(realm.where(ResponseZone.class).findAll()); } }); } 

    Ho preso la libertà e ho rappresentato il tuo Single come Single , considerandolo non osservabile, non ascoltare i cambiamenti, c'è solo un evento e questo è l'elenco stesso. Così l'invio tramite un ObservableEmitter non ha davvero senso in quanto non emette events.

    Pertanto, questo è il motivo per cui ho detto: il tuo livello dati non è reattivo. Non stai ascoltando i cambiamenti. Stai solo ottenendo i dati direttamente e non ti viene mai notificato alcuna modifica; nonostante l'utilizzo di Rx.


    Ho disegnato alcune immagini in vernice per illustrare il mio punto. (blu significa effetti collaterali)

    pulito architettura non reattiva

    nel tuo caso, chiama un'operazione unica per recuperare i dati da più origini dati (cache, locali, remoti). Una volta ottenuta, non si ascolta le modifiche; tecnicamente se si modificano i dati in un posto e in un altro luogo, l'unico modo per aggiornare è quello di "forzare la cache per recuperare manualmente i nuovi dati"; per cui devi sapere che hai modificato i dati da qualche altra parte . Per cui hai bisogno di un modo per call direttamente una callback o submit un messaggio / evento – una notifica per la modifica.

    Quindi, in un certo senso, è necessario creare un evento di notifica di invalidazione della cache. E se lo ascolti, la soluzione potrebbe essere ancora retriggers. Salvo che lo farai manualmente.

    ————————————————– ——————–

    Considerando che Realm è già un'origine dati retriggers (simile a SQLBrite per SQLite), è in grado di fornire notifiche di modifica con le quali è ansible "invalidare la cache".

    Infatti, se l'origine dati locale è l'unica fonte di dati, e qualunque scrittura da networking è una modifica da ascoltare, la tua "cache" può essere scritta come replay(1).publish().refCount() (ripetere i dati più recenti per i nuovi abbonati, sostituire i dati con nuovi se nuovi dati vengono valutati) che è RxReplayingShare .

    architettura pulita reattiva

    Utilizzando un Scheduler creato dal looper di un thread gestore , è ansible ascoltare le modifiche nel reame su un thread di background, creando un'origine dati retriggers che restituisce aggiornate copie non gestite che è ansible passare tra i thread (anche se la mapping direttamente i templates di dominio immutabili sono preferiti per copyFromRealm() se si sceglie questo path – il path è architettura pulita).

     return io.reactivex.Observable.create(new ObservableOnSubscribe<List<ResponseZone>>() { @Override public void subscribe(ObservableEmitter<List<ResponseZone>> emitter) throws Exception { final Realm observableRealm = Realm.getDefaultInstance(); final RealmResults<ResponseZone> results = observableRealm.where(ResponseZone.class).findAllAsync(); final RealmChangeListener<RealmResults<ResponseZone>> listener = results -> { if(!emitter.isDisposed()) { if(results.isValid() && results.isLoaded()) { emitter.onNext(observableRealm.copyFromRealm(results)); } } }; emitter.setDisposable(Disposables.fromRunnable(() -> { if(results.isValid()) { results.removeChangeListener(listener); } observableRealm.close(); })); results.addChangeListener(listener); // initial value will be handled by async query } }).subscribeOn(looperScheduler).unsubscribeOn(looperScheduler); 

    Quando looperatore del looper viene ottenuto come

      handlerThread = new HandlerThread("LOOPER_SCHEDULER"); handlerThread.start(); synchronized(handlerThread) { looperScheduler = AndroidSchedulers.from(handlerThread.getLooper()); } 

    Ecco come creare un'architettura pulita retriggers usando Realm.


    AGGIUNTO:

    LooperScheduler è necessario solo se si intende effettivamente applicare l'architettura pulita in Realm. Questo perché Realm per impostazione predefinita ti incoraggia a utilizzare i tuoi oggetti dati come templates di dominio e, come vantaggio, offre le visualizzazioni locali di thread-fonte che vengono mutate quando vengono aggiornate; ma Architettura pulita dice che dovresti utilizzare templates di dominio immutabili (indipendentemente dal tuo livello di dati). Quindi, se si desidera creare un'architettura pulita retriggers in cui si copia da Realm su un thread di background in qualsiasi momento in cui si modifica il Realm, è necessario disegnare looper (o osservare in un thread di background, ma eseguire la copia da un Realm aggiornato sugli Schedulers.io() ).

    Con Realm, in genere si desidera utilizzare RealmObjects come templates di dominio e fare affidamento su una valutazione pigra. In questo caso, non si utilizza copyFromRealm() e non mappare RealmResults in altro; ma puoi esporlo come LiveData o LiveData .

    Potete leggere cose relative su questo qui .

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