Questa pagina presenta diverse best practice e consigli sull'architettura. Adottali per migliorare la qualità, la robustezza e la scalabilità della tua app. Inoltre, semplificano la manutenzione e il test dell'app.
Le best practice riportate di seguito sono raggruppate per argomento. Ognuna ha una priorità che riflette la forza del consiglio. L'elenco delle priorità è il seguente:
- Vivamente consigliato: implementa questa pratica a meno che non sia in contrasto fondamentale con il tuo approccio.
- Consigliato: è probabile che questa pratica migliori la tua app.
- Facoltativo: questa pratica può migliorare la tua app in determinate circostanze.
Architettura a livelli
L'architettura a livelli consigliata favorisce la separazione delle responsabilità. Gestisce l'interfaccia utente dai modelli di dati, rispetta il principio della singola fonte attendibile, e segue i principi del flusso di dati unidirezionale. Di seguito sono riportate alcune best practice per l'architettura a livelli:
| Suggerimento | Descrizione |
|---|---|
| Utilizza un livello dati chiaramente definito data layer.
Vivamente consigliato |
Il livello dati espone i dati dell'applicazione al resto dell'app e contiene la maggior parte della logica di business dell'app.
|
| Utilizza un livello UI chiaramente definito.
Vivamente consigliato |
Il livello UI mostra i dati dell'applicazione sullo schermo e funge da punto principale di interazione dell'utente. Jetpack Compose è il toolkit moderno consigliato per la creazione dell'interfaccia utente della tua app.
|
| Espone i dati dell'applicazione dal livello dati utilizzando un repository.
Vivamente consigliato |
Assicurati che i componenti del livello UI, come i componibili o i ViewModel, non interagiscano direttamente con un'origine dati. Ecco alcuni esempi di origini dati:
|
| Utilizza coroutine e flussi.
Vivamente consigliato |
Utilizza coroutine e flussi per comunicare tra i livelli.
Per saperne di più sulle best practice delle coroutine, consulta Best practice per le coroutine in Android. |
| Utilizza un livello di dominio.
Consigliato nelle app di grandi dimensioni |
Utilizza un livello di dominio con casi d'uso se devi riutilizzare la logica di business che interagisce con il livello dati in più ViewModel o se vuoi semplificare la complessità della logica di business di un particolare ViewModel. |
Livello UI
Il ruolo del livello UI è quello di mostrare i dati dell'applicazione sullo schermo e fungere da punto principale di interazione dell'utente. Di seguito sono riportate alcune best practice per il livello UI:
| Suggerimento | Descrizione |
|---|---|
| Segui il flusso di dati unidirezionale (UDF).
Vivamente consigliato |
Segui i principi del flusso di dati unidirezionale (UDF), in cui i ViewModel espongono lo stato dell'UI utilizzando il pattern Observer e ricevono azioni dall'UI tramite chiamate di metodi. |
| Utilizza i ViewModel AAC se i loro vantaggi si applicano alla tua app.
Vivamente consigliato |
Utilizza i ViewModel AAC per gestire la logica di business e recuperare i dati dell'applicazione per esporre lo stato dell'UI all'UI.
Per saperne di più sulle best practice di ViewModel, consulta Suggerimenti sull'architettura. Per saperne di più sui vantaggi dei ViewModel, consulta Il ViewModel come contenitore dello stato della logica di business. |
| Utilizza la raccolta dello stato dell'UI con riconoscimento del ciclo di vita.
Vivamente consigliato |
Raccogli lo stato dell'UI dall'UI utilizzando il builder di coroutine con riconoscimento del ciclo di vita appropriato, collectAsStateWithLifecycle.
Scopri di più su |
| Non inviare eventi dal ViewModel all'UI.
Vivamente consigliato |
Elabora immediatamente l'evento nel ViewModel e causa un aggiornamento dello stato con il risultato della gestione dell'evento. Per saperne di più sugli eventi dell'UI, consulta Gestire gli eventi di ViewModel. |
| Utilizza un'applicazione a singola attività.
Vivamente consigliato |
Utilizza Navigation 3 per spostarti tra le schermate e creare deep link alla tua app se ne ha più di una. |
| Utilizza Jetpack Compose.
Vivamente consigliato |
Utilizza Jetpack Compose per creare nuove app per smartphone, tablet, dispositivi pieghevoli e Wear OS. |
Il seguente snippet illustra come raccogliere lo stato dell'UI in modo da riconoscere il ciclo di vita:
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
I ViewModel sono responsabili della fornitura dello stato dell'UI e dell'accesso al livello dati. Di seguito sono riportate alcune best practice per i ViewModel:
| Suggerimento | Descrizione |
|---|---|
| Mantieni i ViewModel indipendenti dal ciclo di vita di Android.
Vivamente consigliato |
Nei ViewModel, non conservare un riferimento a nessun tipo correlato al ciclo di vita. Non passare Activity, Context o Resources come dipendenza.
Se qualcosa richiede un Context nel ViewModel, valuta attentamente se si trova nel livello corretto. |
| Utilizza coroutine e flussi.
Vivamente consigliato |
Il ViewModel interagisce con i livelli di dati o di dominio utilizzando quanto segue:
|
| Utilizza i ViewModel a livello di schermata.
Vivamente consigliato |
Non utilizzare i ViewModel in parti riutilizzabili dell'UI. Dovresti utilizzare i ViewModel in:
Per i componibili più complessi o quelli con un comportamento dinamico basato sullo stato, utilizza |
| Utilizza classi di contenitori di stato semplici nei componenti UI riutilizzabili.
Vivamente consigliato |
Utilizza classi di contenitori di stato semplici per gestire la complessità nei componenti UI riutilizzabili. In questo modo, lo stato può essere innalzato e controllato esternamente. |
Non utilizzare AndroidViewModel.
Consigliato |
Utilizza la ViewModel classe, non AndroidViewModel. Non utilizzare la classe Application nel ViewModel. Sposta invece la dipendenza nell'UI o nel livello dati. |
| Espone uno stato dell'UI.
Consigliato |
Fai in modo che i ViewModel espongano i dati all'UI tramite una singola proprietà denominata uiState. Se l'UI mostra più dati non correlati, la VM può esporre più proprietà dello stato dell'UI.
|
Il seguente snippet illustra come esporre lo stato dell'UI da un ViewModel:
@HiltViewModel
class BookmarksViewModel @Inject constructor(
newsRepository: NewsRepository
) : ViewModel() {
val feedState: StateFlow<NewsFeedUiState> =
newsRepository
.getNewsResourcesStream()
.mapToFeedState(savedNewsResourcesState)
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = NewsFeedUiState.Loading
)
// ...
}
Ciclo di vita
Segui le best practice per lavorare con il ciclo di vita di Activity:
| Suggerimento | Descrizione |
|---|---|
Utilizza gli effetti con riconoscimento del ciclo di vita nei componibili anziché sostituire i callback del ciclo di vita di Activity.
Vivamente consigliato |
Non sostituire i metodi del ciclo di vita di
|
Il seguente snippet illustra come eseguire le operazioni in base a un determinato stato del ciclo di vita:
@Composable
fun LocationChangedEffect(
locationManager: LocationManager,
onLocationChanged: (Location) -> Unit
) {
val currentOnLocationChanged by rememberUpdatedState(onLocationChanged)
LifecycleStartEffect(locationManager) {
val listener = LocationListener { newLocation ->
currentOnLocationChanged(newLocation)
}
try {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
1000L,
1f,
listener,
)
} catch (e: SecurityException) {
// TODO: Handle missing permissions
}
onStopOrDispose {
locationManager.removeUpdates(listener)
}
}
}
Gestire le dipendenze
Segui le best practice quando gestisci le dipendenze tra i componenti:
| Suggerimento | Descrizione |
|---|---|
| Utilizza l'iniezione delle dipendenze.
Vivamente consigliato |
Utilizza le best practice per l'iniezione delle dipendenze, principalmente l'iniezione del costruttore, quando possibile. |
| Definisci l'ambito di un componente quando necessario.
Vivamente consigliato |
Definisci l'ambito di un contenitore di dipendenze quando il tipo contiene dati modificabili che devono essere condivisi o il tipo è costoso da inizializzare ed è ampiamente utilizzato nell'app. |
| Utilizza Hilt.
Consigliato |
Utilizza Hilt o l'iniezione manuale delle dipendenze nelle app semplici. Utilizza Hilt se il tuo progetto è abbastanza complesso, ad esempio se include uno dei seguenti elementi:
|
Test
Di seguito sono riportate alcune best practice per i test:
| Suggerimento | Descrizione |
|---|---|
| Scopri cosa testare.
Vivamente consigliato |
A meno che il progetto non sia semplice come un'app "Hello World", esegui il test. Come minimo, includi quanto segue:
|
| Preferisci i fake ai mock.
Vivamente consigliato |
Per saperne di più sull'utilizzo dei fake, consulta Utilizzare i test double in Android. |
| Testa i StateFlow.
Vivamente consigliato |
Quando testi StateFlow, procedi nel seguente modo:
|
Per saperne di più, consulta Cosa testare in Android e Testa il layout di Compose.
Modelli
Segui queste best practice quando sviluppi modelli nelle tue app:
| Suggerimento | Descrizione |
|---|---|
| Crea un modello per livello nelle app complesse.
Consigliato |
Nelle app complesse, crea nuovi modelli in diversi livelli o componenti quando ha senso. Considera i seguenti esempi:
|
Convenzioni di denominazione
Quando assegni un nome al codebase, devi tenere presente le seguenti best practice:
| Suggerimento | Descrizione |
|---|---|
| Assegnazione di nomi ai metodi.
Facoltativo |
Utilizza frasi verbali per assegnare un nome ai metodi, ad esempio makePayment(). |
| Assegnazione di nomi alle proprietà.
Facoltativo |
Utilizza frasi nominali per assegnare un nome alle proprietà, ad esempio inProgressTopicSelection. |
| Assegnazione di nomi agli stream di dati.
Facoltativo |
Quando una classe espone uno stream di flussi o qualsiasi altro stream, la convenzione di denominazione è get{model}Stream, ad esempio getAuthorStream(): Flow<Author>.
Se la funzione restituisce un elenco di modelli, utilizza il nome del modello al plurale: getAuthorsStream(): Flow<List<Author>>. |
| Assegnazione di nomi alle implementazioni delle interfacce.
Facoltativo |
Utilizza nomi significativi per le implementazioni delle interfacce. Utilizza Default come prefisso se non riesci a trovare un nome migliore. Ad esempio, per un'interfaccia NewsRepository, potresti avere un OfflineFirstNewsRepository o un InMemoryNewsRepository. Se non riesci a trovare un nome appropriato, utilizza DefaultNewsRepository.
Aggiungi il prefisso Fake alle implementazioni fake, ad esempio FakeAuthorsRepository. |
Risorse aggiuntive
Per saperne di più sull'architettura di Android, consulta le seguenti risorse aggiuntive: