Devo dire la verità, prima di adottare gli observable sul progetto OpenLapTimer ero decisamente scettico, ma poi…

Partiamo dall’inizio

La situazione era la seguente: sul progetto Java del lap timer da circuito più “indie” che conosca (il mio) regnava il caos. La gestione di alcune procedure come ad esempio il riconoscimento della pressione di un tasto fisico sul device (RaspberryPi) avveniva tramite Thread: fin qui nulla di strano direte voi, invece tra timer, thread e procedure eseguite da thread diversi, il device invece di iniziare a misurare il tempo sul giro, si spegneva in modo decisamente randomico 🙁

Il device ha un solo tasto per gestire tutte le operazioni:

  • 1 tocco: start e pausa della misurazione
  • 2 tocchi: termina la registrazione dei tempi e dell’intera sessione di giri
  • long press (10 sec): spegne il device

Ho iniziato quindi a debuggare l’applicazione per capire il motivo di questi comportamenti strani, ma nulla da fare! Quel codice apparentemente semplice e lineare in realtà nascondeva delle insidie.

La svolta

Ascoltando i suggerimenti (oserei definirle quasi pressioni psicologiche) di Emanuele ho deciso di approcciare il problema con un altro strumento per cambiare il mio punto di vista sul problema: usare gli Observable che tanto abbiamo usato in passato su Web con Angular e Javascript.

Abbiamo spiegato cosa sono gli Observable in un precedente articolo del blog:

Observable su Java ha un solo nome: RxJava! (https://github.com/ReactiveX/RxJava)
Citando la descrizione del repo:
“RxJava – Reactive Extensions for the JVM – a library for composing asynchronous and event-based programs using observable sequences for the Java VM.”

La potenza di RxJava sta nel poter concatenare i diversi operatori disponibili nella libreria per creare un flusso, uno stream, personalizzato secondo le diverse esigenze. Con una sola riga è possibile assegnare una intera elaborazione ad un thread specifico, rendendolo quindi asincrono, liberando il thread corrente da un carico di lavoro oneroso.

Ovviamente non è tutto così semplice ed immediato, per poter entrare nell’ottica degli Observable, soprattutto quando bisogna emettere valori e non semplicemente leggerli, serve tempo. Online si trovano molte risorse su Observable e RxJava, di cui il 99% parla di come consumare un flusso di dati, su OpenLapTimer serviva soprattutto emettere valori e per scovare l’1% di quella documentazione è servita altra pazienza.
Una delle risorse più utili per me è stata questa: http://escoffier.me/rxjava-hol/
L’autore spiega passo passo con esercizi ed esempi ogni casistica ed è stato davvero illuminante.

Una volta compreso il meccanismo e il concetto di Observer/Observable e stream è facile vedere tutto in termini di flusso di dati, così ho sostituito molte interazioni tra model e controller o parti diverse di business logic con gli observable, il risultato? Pulizia del codice (che si traduce in manutenibilità) semplicità ed immediatezza alla lettura del codice (che si traduce in debug rapido).

Per capire meglio vi riporto un esempio concreto: la visualizzazione sul display del lap timer del tempo del giro attualmente in corso in cui sono coinvolte 3 entità:

  • Controller: colui che orchestra, gestisce i dati
  • View: la renderizzazione su display del tempo sul giro
  • LapTimer: business logic che calcola il tempo sul giro

Versione pre Observable
Classe Controller (veniva gestito con un Timer Java)

lapTimeTimer = new Timer();
lapTimeTimer.schedule(new TimerTask() {
  @Override
  public void run() {
    timerView.updateLapTime(lapTimer.getCurrentLapTime(), TimeUtils.DEC_PRECISION);
  }
}, 0, 5);

Versione post Observable
Classe Controller (in fase di init)

lapTimeDisposable = laptimer.getCurrentLapTimeFlowable()
  .subscribe(lapTime -> {
    String timeString = TimeUtils.getHumanReadableLapTime(lapTime, TimeUtils.DEC_PRECISION);
    timerView.setLapTimeLabelText(timeString);
});

Classe LapTimer

Observable.interval(5L, TimeUnit.MILLISECONDS)
  .observeOn(Schedulers.io())
  .doOnNext(i -> {
    currentLapTimeSource.onNext(this.getCurrentLapTime());    
});

Ad uno sguardo superficiale la differenza potrebbe essere poca o addirittura nulla.
In realtà il beneficio è che si sgrava il Controller da compiti che sono di competenza del LapTimer: gestire il tempo che passa ed emettere quindi il valore del tempo sul giro attuale, mentre il Controller deve solo inviare il comando di aggiornamento della View. In questo modo aumenta la chiarezza sulle responsabilità degli oggetti in campo e il tutto risulta più chiaro in stile MVC.
Seconda cosa non meno importante è che in questo modo abbiamo eliminato il Timer Java a favore di un observable, il che ci consente di variare la gestione del thread usato per l’elaborazione cambiando una sola riga, cosa che prima non era possibile e che poteva generare Eccezioni.

Concludendo

L’adozione infine di dagger per la gestione delle dipendenze e l’implementazione più aderente del paradigma MVC hanno reso il tutto ancora più lineare e semplice, ma di questo ne parleremo la prossima volta in un nuovo articolo…

La cosa che mi ha compito maggiormente di questa esperienza è come la soluzione ad un problema legato ad una applicazione Java in esecuzione su un RaspberryPi sia arrivata da una nostra esperienza pregressa su una libreria Javascript. Questo ci deve far pensare molto, e ci deve spronare a esplorare sempre nuovi linguaggi e framework, non tanto per usarli direttamente, ma soprattutto per imparare nuovi paradigmi ed approcci che poi possiamo calare nella nostra realtà quotidiana.

Quindi per finire Observable si o no? Decisamente SI 🙂