Questa pagina presenta diverse best practice e consigli per l'architettura. Adottale per migliorare la qualità, la robustezza e la scalabilità della tua app. Inoltre, facilitano la manutenzione e il test dell'app.
Le best practice riportate di seguito sono raggruppate per argomento. Ognuno di questi ha una priorità che riflette la raccomandazione del team. L'elenco di priorità è il seguente:
- Consigliato vivamente:ti consigliamo di implementare questa pratica, a meno che non entri in conflitto fondamentale con il tuo approccio.
 - Consigliato:questa pratica potrebbe migliorare la tua app.
 - Facoltativo:questa pratica può migliorare la tua app in determinate circostanze.
 
Architettura a più livelli
La nostra architettura a livelli consigliata favorisce la separazione dei problemi. Determina l'interfaccia utente da modelli dei dati, è conforme al principio della singola fonte attendibile e segue i principi del flusso di dati unidirezionali. Di seguito sono riportate alcune best practice per l'architettura a più 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.
      
  | 
  
| Il livello dati deve esporre i dati dell'applicazione utilizzando un repository.
     Fortemente consigliato  | 
    I componenti del livello dell'interfaccia utente, come gli elementi componibili, le attività o i ViewModel, non devono interagire 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. | 
| Utilizza un livello di dominio.
 Consigliato nelle app di grandi dimensioni  | 
    Utilizza un livello di dominio, casi d'uso, se devi riutilizzare la logica di business che interagisce con il livello di dati in più ViewModel o se vuoi semplificare la complessità della logica di business di un determinato ViewModel | 
Livello UI
Il ruolo del 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 | 
|---|---|
| Segui Unidirectional Data Flow (UDF).
     Vivamente consigliato  | 
    Segui i principi del flusso di dati unidirezionale (UDF), in cui i ViewModel espongono lo stato dell'interfaccia utente utilizzando il pattern di osservazione e ricevono azioni dall'interfaccia utente tramite chiamate di metodo. | 
| Utilizza i ViewModel AAC 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 all'interfaccia utente (Compose o Android Views).
     Consulta altre best practice per ViewModel qui. Scopri i vantaggi dei ViewModel qui.  | 
  
| Utilizza la raccolta dello stato dell'interfaccia utente che tiene conto del ciclo di vita.
     Vivamente consigliato  | 
    Raccogli lo stato dell'interfaccia utente utilizzando il builder di coroutine che tiene conto del ciclo di vita appropriato: repeatOnLifecycle nel sistema View e collectAsStateWithLifecycle in Jetpack Compose.
    Scopri di più su   Scopri di più su    | 
  
| Non inviare eventi dal ViewModel alla UI.
     Vivamente consigliato  | 
    Elabora immediatamente l'evento nel ViewModel e provoca un aggiornamento dello stato con il risultato della gestione dell'evento. Scopri di più sugli eventi UI qui. | 
| Utilizza un'applicazione con una singola attività.
     Consigliato  | 
    Utilizza Navigation Fragments o Navigation Compose per spostarti tra le schermate e creare un link diretto alla tua app se ha più di una schermata. | 
| Utilizza Jetpack Compose.
     Consigliato  | 
    Usa Jetpack Compose per creare nuove app per smartphone, tablet, pieghevoli e Wear OS. | 
Il seguente snippet illustra come raccogliere lo stato dell'interfaccia utente in modo consapevole 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()
}
ViewModel
I ViewModel sono responsabili di fornire lo stato dell'interfaccia utente e l'accesso al livello di dati. Di seguito sono riportate alcune best practice per i ViewModel:
| Consiglio | Descrizione | 
|---|---|
| I ViewModel devono essere indipendenti dal ciclo di vita di Android.
     Vivamente consigliato  | 
    I ViewModel non devono contenere un riferimento a nessun tipo relativo al ciclo di vita. Non passare Activity, Fragment, Context o Resources come dipendenza.
Se qualcosa richiede un Context nel ViewModel, devi valutare attentamente se si trova nel livello corretto. | 
  
| Utilizza coroutine e flussi.
     Vivamente consigliato  | 
    ViewModel interagisce con i livelli di dati o di dominio utilizzando: 
  | 
  
| Utilizza ViewModels a livello di schermo.
     Fortemente consigliato  | 
    Non utilizzare i ViewModel in componenti dell'interfaccia utente riutilizzabili. Dovresti utilizzare i ViewModel in: 
  | 
  
| Utilizza le classi di contenitori di stato semplici nei componenti dell'interfaccia utente riutilizzabili.
     Fortemente consigliato  | 
    Utilizza le classi di contenitori di stato semplici per gestire la complessità nei componenti dell'interfaccia utente riutilizzabili. In questo modo, lo stato può essere sollevato e controllato esternamente. | 
Non utilizzare AndroidViewModel.
    Consigliato  | 
    Utilizza il corso ViewModel, non AndroidViewModel. La classe Application non deve essere utilizzata in ViewModel. Sposta invece la dipendenza nell'interfaccia utente o nel livello di 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à di stato dell'interfaccia utente.
      
  | 
  
Il seguente snippet 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 l'utilizzo del ciclo di vita di Android:
| Consiglio | Descrizione | 
|---|---|
| Non eseguire l'override dei metodi di ciclo di vita in Activities o Fragment.
     Vivamente consigliato  | 
    Non eseguire l'override di metodi di ciclo di vita come onResume in Activities o Fragments. Utilizza invece LifecycleObserver. Se l'app deve eseguire un'operazione quando il ciclo di vita raggiunge un determinato Lifecycle.State, utilizza l'API repeatOnLifecycle. | 
  
Lo snippet seguente illustra come eseguire operazioni in base a 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'iniezione di dipendenze.
     Vivamente consigliato  | 
    Utilizza le best practice di iniezione di dipendenze, in particolare l'iniezione del costruttore, se possibile. | 
| Limita l'ambito a un componente quando necessario.
     Vivamente consigliato  | 
    Assegna l'ambito a un container delle dipendenze quando il tipo contiene dati modificabili che devono essere condivisi o quando il tipo è costoso da inizializzare ed è ampiamente utilizzato nell'app. | 
| Usa Hilt.
     Consigliato  | 
    Utilizza Hilt o l'inserimento manuale delle dipendenze nelle app semplici. Utilizza Hilt se il tuo progetto è abbastanza complesso. Ad esempio, se hai:
      
  | 
  
Test
Di seguito sono riportate alcune best practice per i test:
| Consiglio | Descrizione | 
|---|---|
| Sai cosa testare.
 Vivamente consigliato  | 
    A meno che il progetto non sia approssimativamente semplice come un'app Hello World, devi testarlo, almeno con: 
  | 
  
| Preferisci i falsi alle simulazioni.
 Fortemente consigliato  | 
    Scopri di più nella documentazione sull'utilizzo del test Doppio in Android. | 
| Testa StateFlows.
 Vivamente consigliato  | 
    Durante il test di StateFlow:
      
  | 
  
Per ulteriori informazioni, consulta la guida Cosa testare in Android DAC.
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 livelli o componenti diversi quando necessario. 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  | 
    I metodi devono essere una frase di verbi. Ad esempio, makePayment(). | 
  
| Denominazione delle proprietà.
     Facoltativo  | 
    Le proprietà devono essere una frase nominale. Ad esempio, inProgressTopicSelection. | 
  
| Assegnare un nome agli stream di dati.
     Facoltativo  | 
    Quando una classe espone uno stream di Flow, LiveData o qualsiasi altro stream, 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 per le implementazioni delle interfacce devono essere significativi. Avere 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.
    Le implementazioni false devono essere precedute dal prefisso Fake, come in FakeAuthorsRepository. |