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 . |