Questa pagina presenta diversi consigli e best practice per l'architettura. Adottali per migliorare qualità, robustezza e scalabilità della tua app. Inoltre, semplificano la manutenzione e il test della tua app.
Le best practice riportate di seguito sono raggruppate per argomento. Ognuno ha una priorità che riflette la forza che il team lo consiglia. L'elenco delle priorità è il seguente:
- Vivamente consigliata: dovresti implementare questa pratica, a meno che non entri in conflitto con il tuo approccio.
- Consigliato: è probabile che questa pratica 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 dei problemi. Gestisce l'interfaccia utente da modelli di dati, è conforme al principio dell'unica fonte attendibile e segue i principi del flusso di dati unidirezionale. Ecco alcune best practice per l'architettura a più livelli:
Consiglio | Descrizione |
---|---|
Utilizza un livello dati chiaramente definito.
Fortemente 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 ben definito.
Fortemente consigliato |
Il livello UI mostra i dati dell'applicazione sullo schermo e funge da punto principale di interazione dell'utente.
|
Il livello dati deve esporre i dati dell'applicazione utilizzando un repository.
Fortemente consigliato |
I componenti nel livello dell'interfaccia utente, come i componibili, le attività o i ViewModel, non devono interagire direttamente con un'origine dati. Ecco alcuni esempi di origini dati:
|
Utilizza coroutine e flussi.
Fortemente consigliato |
Utilizza coroutine e flussi per comunicare tra i livelli. |
Utilizza un livello dominio.
Consigliato nelle app di grandi dimensioni |
Per casi d'uso, utilizza un livello dominio se devi riutilizzare la logica di business che interagisce con il livello dati su più ViewModel oppure se vuoi semplificare la complessità della logica di business di un determinato ViewModel. |
Livello UI
Il livello UI è visualizzare i dati dell'applicazione sullo schermo e fungere da punto di interazione principale con l'utente. Ecco alcune best practice per il livello UI:
Consiglio | Descrizione |
---|---|
Consulta la sezione Flusso di dati unidirezionale (UDF).
Fortemente consigliato |
Segui i principi del Flusso di dati unidirezionale (UDF), in cui i ViewModel espongono lo stato dell'interfaccia utente utilizzando il pattern osservatore e ricevono azioni dall'interfaccia utente tramite chiamate al metodo. |
Utilizza AAC ViewModels se i relativi vantaggi si applicano alla tua app.
Fortemente consigliato |
Utilizza i ViewModel AAC per gestire la logica di business e recuperare i dati dell'applicazione per esporre lo stato dell'interfaccia utente (Scrivi o Visualizzazioni Android).
Consulta altre best practice per ViewModel qui. Scopri i vantaggi di ViewModels qui. |
Utilizza la raccolta dello stato dell'UI sensibile al ciclo di vita.
Fortemente consigliato |
Raccogli lo stato della UI dalla UI utilizzando il builder di coroutine sensibile al ciclo di vita appropriato: repeatOnLifecycle nel sistema di visualizzazione e collectAsStateWithLifecycle in Jetpack Compose.
Scopri di più su Scopri di più su |
Non inviare eventi da ViewModel all'interfaccia utente.
Fortemente consigliato |
Elabora l'evento immediatamente nel ViewModel e causa un aggiornamento dello stato con il risultato della gestione dell'evento. Scopri di più sugli eventi UI. |
Utilizza un'applicazione per singola attività.
Consigliato |
Utilizza Frammenti di navigazione o Scrittura navigazione per spostarti tra le schermate e il link diretto alla tua app se l'app ha più di una schermata. |
Utilizza Jetpack Compose.
Consigliato |
Utilizza Jetpack Compose per creare nuove app per smartphone, tablet, pieghevoli e Wear OS. |
Lo snippet seguente illustra come raccogliere lo stato della UI in un modo rispettoso del ciclo di vita:
Visualizzazioni
class MyFragment : Fragment() {
private val viewModel: MyViewModel by viewModel()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
// Process item
}
}
}
}
}
Scrivi
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
Visualizza modello
I ViewModel sono responsabili della fornitura dello stato dell'interfaccia utente e dell'accesso al livello dati. Di seguito sono riportate alcune best practice per i modelli ViewModel:
Consiglio | Descrizione |
---|---|
I modelli ViewModel devono essere indipendenti dal ciclo di vita di Android.
Fortemente consigliato |
I modelli ViewModel non devono contenere un riferimento a nessun tipo di ciclo di vita. Non trasmettere Activity, Fragment, Context o Resources come dipendenza.
Se qualcosa richiede un Context in ViewModel, dovresti valutare fortemente se questo si trova nel livello giusto. |
Utilizza coroutine e flussi.
Fortemente consigliato |
ViewModel interagisce con i livelli di dati o di dominio utilizzando:
|
Utilizzare ViewModels a livello di schermo.
Fortemente consigliato |
Non utilizzare ViewModels in parti riutilizzabili dell'interfaccia utente. Devi utilizzare ViewModels in:
|
Utilizza le classi con stato normale nei componenti riutilizzabili dell'interfaccia utente.
Fortemente consigliato |
Utilizza classi standard per gestire la complessità nei componenti riutilizzabili dell'interfaccia utente. In questo modo, lo stato può essere issato e controllato esternamente. |
Non utilizzare AndroidViewModel .
Consigliato |
Utilizza il corso ViewModel , non AndroidViewModel . La classe Application non deve essere utilizzata nel ViewModel. Sposta invece la dipendenza nell'interfaccia utente o nel livello dati. |
Esponi uno stato dell'interfaccia utente.
Consigliato |
I ViewModel devono esporre i dati all'interfaccia utente tramite una singola proprietà denominata uiState . Se l'interfaccia utente mostra più dati non correlati, la VM può esporre più proprietà dello stato dell'interfaccia utente.
|
Lo snippet seguente illustra come esporre lo stato dell'interfaccia utente 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
Di seguito sono riportate alcune best practice per lavorare con il ciclo di vita Android:
Consiglio | Descrizione |
---|---|
Non eseguire l'override dei metodi del ciclo di vita in Attività o Frammenti.
Fortemente consigliato |
Non eseguire l'override dei metodi del ciclo di vita come onResume in Attività o Frammenti. Utilizza LifecycleObserver . Se l'app deve eseguire delle operazioni quando il ciclo di vita raggiunge un determinato Lifecycle.State , utilizza l'API repeatOnLifecycle . |
Lo snippet seguente descrive come eseguire le operazioni in un determinato stato del ciclo di vita:
Visualizzazioni
class MyFragment: Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewLifecycleOwner.lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
// ...
}
override fun onPause(owner: LifecycleOwner) {
// ...
}
}
}
}
Scrivi
@Composable
fun MyApp() {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(lifecycleOwner, ...) {
val lifecycleObserver = object : DefaultLifecycleObserver {
override fun onStop(owner: LifecycleOwner) {
// ...
}
}
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycleOwner.lifecycle.removeObserver(lifecycleObserver)
}
}
}
Gestire le dipendenze
Esistono diverse best practice da seguire per gestire le dipendenze tra i componenti:
Consiglio | Descrizione |
---|---|
Utilizza l'inserimento delle dipendenze.
Fortemente consigliato |
Utilizza le best practice per l'inserimento delle dipendenze, principalmente l'inserimento dei costruttori, se possibile. |
Limita l'ambito a un componente quando necessario.
Fortemente consigliato |
Assegna l'ambito a un container dipendenze quando il tipo contiene dati modificabili che devono essere condivisi o è costoso da inizializzare ed è ampiamente utilizzato nell'app. |
Usa Hilt.
Consigliato |
Utilizza Hilt o l'inserimento manuale delle dipendenze nelle app semplici. Usa Hilt se il progetto è abbastanza complesso. Ad esempio, se hai:
|
Test
Di seguito sono riportate alcune best practice per i test:
Consiglio | Descrizione |
---|---|
Scopri cosa testare.
Fortemente consigliato |
A meno che il progetto non sia all'incirca semplice come un'app Hello World, dovresti testarlo almeno con:
|
Preferisci i falsi a quelli delle simulazioni.
Fortemente consigliato |
Per saperne di più, consulta la documentazione su come utilizzare le copie di test doppie nella documentazione di Android. |
Test StateFlows.
Fortemente consigliato |
Durante il test di StateFlow :
|
Per ulteriori informazioni, consulta la guida sui test dei DAC per Android.
Modelli
Quando sviluppi modelli nelle tue app, devi osservare queste best practice:
Consiglio | Descrizione |
---|---|
Crea un modello per livello nelle app complesse.
Consigliato |
Nelle app complesse, crea nuovi modelli in diversi livelli o componenti quando appropriato. Considera i seguenti esempi:
|
Convenzioni di denominazione
Quando assegni un nome al codebase, tieni presente le seguenti best practice:
Consiglio | Descrizione |
---|---|
Metodi di denominazione.
Facoltativo |
I metodi devono essere una frase verbale. Ad esempio, makePayment() . |
Denominazione delle proprietà.
Facoltativo |
Le proprietà devono essere una frase sostantivo. Ad esempio, inProgressTopicSelection . |
Denominare flussi di dati.
Facoltativo |
Quando una classe espone uno stream Flow, LiveData o qualsiasi altro flusso, la convenzione di denominazione è get{model}Stream() . Ad esempio, getAuthorStream(): Flow<Author>
Se la funzione restituisce un elenco di modelli, il nome del modello deve essere al plurale: getAuthorsStream(): Flow<List<Author>> |
Implementazioni delle interfacce di denominazione.
Facoltativo |
I nomi delle implementazioni delle interfacce devono essere significativi. Utilizza Default come prefisso se non è possibile trovare un nome migliore. Ad esempio, per un'interfaccia NewsRepository , potresti avere un elemento OfflineFirstNewsRepository o InMemoryNewsRepository . Se non riesci a trovare un nome appropriato, utilizza DefaultNewsRepository .
Le implementazioni false devono essere precedute dal prefisso Fake , come in FakeAuthorsRepository . |