Concetti e architettura di Remote Compose

Remote Compose è una libreria Jetpack che ti consente di creare UI con API in stile Compose nella tua app e di visualizzarla nei processi di sistema, come i widget delle app o i widget di Wear OS. La tua app registra la UI come documento compatto e un player nel processo host ne esegue il rendering e gestisce le interazioni, le modifiche di stato e le animazioni localmente, senza richiamare la tua app.

Questa guida spiega l'architettura di Remote Compose e le sue differenze rispetto a Jetpack Compose standard. Questa guida si concentra sulla creazione di UI con i componenti.

Casi d'uso di Remote Compose

Remote Compose può essere utilizzato su qualsiasi superficie con un player di supporto. Su Android, alimenta i widget delle app e i widget di Wear OS.

Come funziona: architettura a tre blocchi

La differenza fondamentale di architettura tra Remote Compose e Jetpack Compose standard risiede nel disaccoppiamento della definizione della UI dall'esecuzione in tre blocchi di alto livello:

  • Crea il documento: l'app esegue le funzioni @RemoteComposable per registrare un documento, ovvero una descrizione compatta e serializzata della UI, inclusi il layout, lo stato remoto e le azioni. Quando i dati cambiano, l'app può registrare e inviare un nuovo documento.
  • Trasferisci il documento all'host remoto: la tua app passa il documento all'host, ovvero il processo che lo visualizza, ad esempio un avviatore o Wear OS.
  • Riproduci il documento: un player Remote Compose nell'host esegue il rendering del documento e gestisce il layout, l'input, le modifiche dello stato della UX e le animazioni localmente, senza richiamare la tua app.

Questa architettura offre vantaggi principali che riducono direttamente la latenza:

  • Dimensioni ridotte dei documenti: gli snapshot dei documenti serializzati rimangono compatti e leggeri.
  • Andate e ritorni ridotti: poiché il player host remoto gestisce l'interazione e le modifiche dello stato localmente, le comunicazioni di andata e ritorno con l'applicazione o il server sono ridotte al minimo.

Analogia con pagina web/HTML: Remote Compose funziona in modo simile alla pubblicazione di una pagina web. Il server dell'app genera e invia una struttura di documento/HTML una sola volta, mentre l'host del browser ne esegue il rendering e gestisce le interazioni localmente senza eseguire query costanti sul backend.

Comunicazione tra il processo dell'app e il processo dell'host remoto per Remote Compose
Figura 1. I tre blocchi dell'architettura di Remote Compose

Componenti

Remote Compose fornisce componenti UI di alto livello per strutturare i layout. Utilizza i componenti di Remote Compose e RemoteModifier anziché i loro equivalenti di Compose standard. Questi includono container di layout di base e componenti adattivi progettati specificamente per gestire dimensioni dello schermo dinamiche:

  • Componenti di layout di base: elementi di layout comuni simili a Compose, come RemoteBox, RemoteRow e RemoteColumn.
  • Componenti di layout adattivi: componenti specializzati come RemoteFitBox, RemoteCollapsibleRow e RemoteCollapsibleColumn progettati per adattarsi in modo pulito alle superfici remote.
  • Temi di Material Design: i temi funzionano allo stesso modo per i widget di Wear OS, con una gerarchia di colori e tipografia fornita da RemoteMaterialTheme. In questo modo, puoi implementare un sistema di branding e design coerente nell'app e nei widget.

Disegno personalizzato con RemoteCanvas

Per grafiche personalizzate e animazioni basate sul tempo, utilizza RemoteCanvas, che registra i comandi di disegno eseguiti dal player sull'host. Questa guida si concentra sulla creazione di UI con i componenti.

Differenze principali

Esamina i seguenti concetti di base per capire in che modo Remote Compose gestisce lo stato, la logica e le azioni.

Riferimenti di stato simbolici

In Compose standard, i contenitori di stato contengono valori attivi e leggibili nella memoria locale dell'app, modificati dal codice dell'app (lambda) quando si verificano eventi. In Remote Compose, le dichiarazioni di stato fungono da riferimenti simbolici. Anziché conservare i valori attivi nella memoria locale dell'app, dichiarano associazioni che il player remoto gestisce e aggiorna localmente in fase di runtime. Tieni presente quanto segue quando dichiari lo stato in Remote Compose:

  • Utilizza le funzioni factory di Remote Compose per dichiarare stati come rememberMutableRemoteInt, rememberMutableRemoteFloat o rememberMutableRemoteString.
  • Il codice dell'app non può ispezionare o leggere il valore di una variabile di stato remota direttamente durante la composizione.
  • Il player memorizza e aggiorna questi valori localmente man mano che l'utente interagisce con il documento.

Stato remoto e flusso di controllo Kotlin

Il codice Kotlin viene eseguito solo quando l'app crea il documento. Le istruzioni Kotlin come if, when e for possono modellare il documento in base ai valori noti in quel momento, ad esempio ripetendo una riga per ogni elemento di un elenco, ma non possono reagire a uno stato remoto che cambia in un secondo momento. Tieni presente quanto segue quando lavori in Remote Compose:

  • Logica di branching: il passaggio dinamico della UI viene eseguito utilizzando RemoteStateLayout mappando lo stato a diversi slot RemoteComposable.
  • Valutazione del testo: evita l'interpolazione di stringhe Kotlin standard ("$remoteValue"), che viene valutata solo quando viene creato il documento. Passa invece RemoteString a RemoteText.

Stile dinamico utilizzando RemoteModifier

Lo stile dinamico sull'host non esegue nuovamente la composizione; le variabili di stato remote vengono invece passate direttamente ai modificatori, come RemoteModifier.background(remoteColor).

Contesto del sistema remoto

I provider CompositionLocal come LocalConfiguration o LocalDensity riflettono solo l'ambiente dell'app locale al momento della registrazione del documento. Non si aggiornano dinamicamente sull'host remoto quando la superficie viene ridimensionata, cambia lo stato di visualizzazione o avanza nel tempo. Utilizza invece i valori che il player risolve sull'host in fase di runtime, ad esempio le dimensioni correnti del componente, RemoteConfiguration o RemoteDensity.

Azioni dichiarative

In Compose standard, i gestori dei clic sono lambda che vengono eseguiti nella tua app. L'host non può eseguire il codice della tua app, quindi in Remote Compose devi collegare le azioni, ad esempio con RemoteModifier.clickable(action). Un'azione può aggiornare lo stato remoto direttamente sull'host con valueChange o passare alla tua app o all'host con pendingIntentAction o hostAction. Per eseguire più azioni con un solo clic, utilizza combinedAction.

Confronto dei paradigmi di alto livello

Dominio delle caratteristiche

Jetpack Compose standard

Remote Compose

Eventi di clic ed evento touch

Funzioni lambda sincrone in-process

Oggetti Action serializzati dichiarativi

Logica condizionale

Flusso di controllo if e when di Kotlin

Il codice Kotlin if/when standard funziona normalmente per lo stato statico noto durante la generazione del documento.

La ramificazione dinamica utilizza RemoteStateLayout (per gli alberi della UI).

Loop del flusso di controllo

Cicli for e while standard di Kotlin

I loop Kotlin standard (repeat, for, forEach) vengono eseguiti solo quando viene creato il documento. Non possono eseguire un loop sullo stato remoto dell'host

Host di esecuzione

Procedura di richiesta

Avvio app (AppWidget), widget Wear OS o qualsiasi fattore di forma con un player di supporto

Ciclo di vita del processo

Il processo deve rimanere attivo per la ricomposizione e il rendering

Il processo registra il documento snapshot dell'interfaccia utente; il motore del Remote Player analizza l'albero dei componenti, monitora le mutazioni di stato e ridisegna localmente senza richiamare l'app.

Effetti collaterali

Cicli di vita delle coroutine (LaunchedEffect)

Non disponibile sull'host. La composizione viene eseguita nella tua app per creare il documento, quindi gli effetti delle coroutine non possono controllare la UI remota, a meno che l'integrazione non mantenga aperta una composizione.

Paradigma dello stato

Valori dello stato della memoria locale

Funzioni di fabbrica (rememberMutableRemoteInt(), rememberNamedRemoteColor()) che restituiscono riferimenti di stato simbolici anziché valori non elaborati.