Questa pagina presenta diverse best practice e consigli per l'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. Ciascuna ha una priorità che riflette l'importanza del consiglio. L'elenco delle priorità è il seguente:
- Fortemente consigliato:implementa questa pratica, a meno che non sia in contrasto con il tuo approccio.
- Consigliato:questa pratica probabilmente migliorerà la tua app.
- (Facoltativo):questa pratica può migliorare la tua app in determinate circostanze.
Architettura a più livelli
La nostra architettura a più livelli consigliata favorisce la separazione delle competenze. Deriva l'interfaccia utente dai modelli di dati, rispetta il principio dell'unica fonte attendibile e segue i principi del flusso di dati unidirezionale. Di seguito sono riportate alcune best practice per l'architettura a livelli:
| Consiglio | Descrizione |
|---|---|
| Utilizza un livello dati chiaramente definito.
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 della UI della tua app.
|
| Esporre i dati dell'applicazione dal livello dati utilizzando un repository.
Vivamente consigliato |
Assicurati che i componenti nel livello UI, come i composable 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 per le coroutine, consulta Best practice per le coroutine in Android. |
| Utilizza un livello di dominio.
Consigliata 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 è visualizzare i dati dell'applicazione sullo schermo e fungere da punto principale di interazione dell'utente. Ecco alcune best practice per il livello UI:
| Consiglio | 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 della UI utilizzando il pattern Observer e ricevono azioni dalla UI tramite chiamate di metodi. |
| Utilizza AAC ViewModels se i relativi vantaggi si applicano alla tua app.
Vivamente consigliato |
Utilizza AAC ViewModels per gestire la logica di business e recuperare i dati dell'applicazione per esporre lo stato dell'interfaccia utente all'interfaccia utente.
Per ulteriori informazioni sulle best practice di ViewModel, consulta Suggerimenti per l'architettura. Per saperne di più sui vantaggi di ViewModel, consulta ViewModel come contenitore dello stato della logica di business. |
| Utilizza la raccolta dello stato dell'interfaccia utente in base al ciclo di vita.
Vivamente consigliato |
Raccogli lo stato della UI dalla UI utilizzando il builder di coroutine consapevole del ciclo di vita appropriato, collectAsStateWithLifecycle.
Scopri di più su |
| Non inviare eventi dalla ViewModel alla 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'interfaccia utente, vedi Gestire gli eventi ViewModel. |
| Utilizza un'applicazione con una sola attività.
Vivamente consigliato |
Utilizza Navigazione 3 per spostarti tra le schermate e creare deep link alla tua app se ha più di una schermata. |
| Utilizza Jetpack Compose.
Vivamente consigliato |
Utilizza Jetpack Compose per creare nuove app per smartphone, tablet, dispositivi pieghevoli e Wear OS. |
Il seguente snippet mostra come raccogliere lo stato della UI in modo consapevole del ciclo di vita:
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
Le 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:
| Consiglio | Descrizione |
|---|---|
| Mantieni i ViewModel indipendenti dal ciclo di vita di Android.
Vivamente consigliato |
Nei ViewModels, non conservare un riferimento a nessun tipo correlato al ciclo di vita. Non trasmettere 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 |
ViewModel interagisce con i livelli di dati o di dominio utilizzando quanto segue:
|
| Utilizza ViewModels a livello di schermata.
Vivamente consigliato |
Non utilizzare ViewModels in parti riutilizzabili dell'UI. Devi utilizzare i ViewModel in:
|
| Utilizza classi semplici di contenitore di stato in componenti UI riutilizzabili.
Vivamente consigliato |
Utilizza contenitori di stato semplici per gestire la complessità nei componenti UI riutilizzabili. In questo modo, lo stato può essere sollevato e controllato esternamente. |
Non utilizzare AndroidViewModel.
Consigliato |
Utilizza la classe ViewModel, non AndroidViewModel. Non utilizzare la classe Application in ViewModel. Sposta invece la dipendenza nell'interfaccia utente o nel livello dati. |
| Esporre uno stato dell'interfaccia utente.
Consigliato |
Fai in modo che i tuoi ViewModel espongano i dati alla UI tramite una singola proprietà denominata uiState. Se la UI mostra più dati non correlati, la VM può esporre più proprietà dello stato della UI.
|
Lo snippet seguente mostra come esporre lo stato della 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 dell'attività:
| Consiglio | Descrizione |
|---|---|
Utilizza effetti sensibili al ciclo di vita nei composable anziché eseguire l'override dei callback del ciclo di vita Activity.
Vivamente consigliato |
Non eseguire l'override dei metodi del ciclo di vita
|
Il seguente snippet descrive 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:
| Consiglio | Descrizione |
|---|---|
| Utilizza l'iniezione delle dipendenze.
Vivamente consigliato |
Utilizza le best practice per l'inserimento delle dipendenze, principalmente il constructor injection, quando possibile. |
| Limita l'ambito a un componente, se necessario.
Vivamente consigliato |
Definisci l'ambito di un contenitore di dipendenze quando il tipo contiene dati modificabili che devono essere condivisi o quando l'inizializzazione del tipo è costosa ed è ampiamente utilizzato nell'app. |
| Utilizza Hilt.
Consigliato |
Utilizza Hilt o l'inserimento manuale delle dipendenze nelle app semplici. Utilizza Hilt se il tuo progetto è sufficientemente complesso, ad esempio se include uno dei seguenti elementi:
|
Test
Di seguito sono riportate alcune best practice per i test:
| Consiglio | Descrizione |
|---|---|
| Sapere cosa testare.
Vivamente consigliato |
A meno che il progetto non sia semplice come un'app "Hello World", testalo. Come minimo, includi quanto segue:
|
| Preferisci i fake ai mock.
Vivamente consigliato |
Per ulteriori informazioni sull'utilizzo di test doppi, consulta Utilizzare test doppi in Android. |
| Testa StateFlows.
Vivamente consigliato |
Quando esegui il test di StateFlow, procedi nel seguente modo:
|
Per saperne di più, consulta Cosa testare in Android e Testare il layout di Compose.
Modelli
Segui queste best practice quando sviluppi modelli nelle tue app:
| Consiglio | Descrizione |
|---|---|
| Crea un modello per livello nelle app complesse.
Consigliato |
Nelle app complesse, crea nuovi modelli in livelli o componenti diversi quando ha senso. Considera i seguenti esempi:
|
Convenzioni di denominazione
Quando assegni un nome al tuo codebase, tieni presente le seguenti best practice:
| Consiglio | Descrizione |
|---|---|
| Metodi di denominazione.
Facoltativo |
Utilizza espressioni verbali per denominare i metodi, ad esempio makePayment(). |
| Proprietà di denominazione.
Facoltativo |
Utilizza sintagmi nominali per denominare le proprietà, ad esempio inProgressTopicSelection. |
| Assegnazione di nomi agli stream di dati.
Facoltativo |
Quando una classe espone uno stream di Flow 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>>. |
| Denominazione delle implementazioni delle interfacce.
Facoltativo |
Utilizza nomi significativi per le implementazioni delle interfacce. Utilizza Default come prefisso se non è possibile trovare un nome migliore. Ad esempio, per un'interfaccia NewsRepository, potresti avere un OfflineFirstNewsRepository o un InMemoryNewsRepository. Se non riesci a trovare un nome adatto, utilizza DefaultNewsRepository.
Aggiungi il prefisso Fake alle implementazioni fittizie, come in FakeAuthorsRepository. |
Risorse aggiuntive
Per ulteriori informazioni sull'architettura di Android, consulta le seguenti risorse aggiuntive: