Questa pagina presenta una serie di best practice e consigli per l'architettura. Adottale per migliorare la qualità, la robustezza e la scalabilità della tua app. Inoltre, semplifica la manutenzione e il test della tua app.
Le best practice riportate di seguito sono raggruppate per argomento. Ognuna ha una priorità che riflette in che misura il team lo consiglia. L'elenco di priorità è il seguente:
- Vivamente consigliato:è consigliabile implementare questa pratica a meno che non entri in conflitto. in base al tuo approccio.
- Consigliato: questa prassi potrebbe migliorare la tua app.
- (Facoltativo) In determinate circostanze, questa prassi può migliorare la tua app.
Architettura a più livelli
La nostra architettura a più livelli consigliata favorisce la separazione dei problemi. it ottimizza l'UI da modelli di dati, rispetta il principio della singola fonte attendibile, e segue i principi del flusso di dati unidirezionale. Ecco alcune delle migliori 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 chiaramente 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 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.
Fortemente 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, i casi d'uso, se devi riutilizzare la logica di business che interagisce con il livello dati su più ViewModel o vuoi semplificare la complessità della logica di business di un particolare ViewModel |
Livello UI
Il livello UI ha il compito di visualizzare i dati dell'applicazione sullo schermo. e fungono da punto principale di interazione dell'utente. Ecco alcune best practice per il livello UI:
Consiglio | Descrizione |
---|---|
Segui il flusso di dati unidirezionali (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 loro vantaggi si applicano alla tua app.
Fortemente consigliato |
Utilizza ViewModels AAC per gestire la logica di business e recuperare i dati dell'applicazione per esporre lo stato dell'interfaccia utente (Compose o Android View).
Scopri altre best practice per ViewModel qui. Scopri i vantaggi di ViewModels qui. |
Utilizza la raccolta degli stati dell'interfaccia utente sensibile al ciclo di vita.
Fortemente consigliato |
Raccogli lo stato della UI dalla UI utilizzando il generatore di coroutine appropriato per il ciclo di vita: repeatOnLifecycle nel sistema di vista e collectAsStateWithLifecycle in Jetpack Compose.
Scopri di più su Scopri di più su |
Non inviare eventi dal ViewModel alla UI.
Fortemente consigliato |
Elaborare immediatamente l'evento nel ViewModel e causare un aggiornamento dello stato con il risultato della gestione dell'evento. Scopri di più sugli eventi UI qui. |
Utilizza un'applicazione a singola attività.
Consigliato |
Utilizza Frammenti di navigazione o Scrittura di 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 |
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 un ambiente sensibile al ciclo di vita. modo:
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
}
}
}
}
}
Compose
@Composable
fun MyScreen(
viewModel: MyViewModel = viewModel()
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
}
ViewModel
I ViewModel devono fornire lo stato dell'interfaccia utente e l'accesso ai livello dati. Ecco alcune best practice per i ViewModel:
Consiglio | Descrizione |
---|---|
I ViewModel devono essere indipendenti dal ciclo di vita di Android.
Fortemente consigliato |
I ViewModel non devono contenere un riferimento a nessun tipo di ciclo di vita. Non trasferire Activity, Fragment, Context o Resources come dipendenza.
Se qualcosa richiede un Context nel ViewModel, devi valutare vivamente se si trova nel livello giusto. |
Utilizza coroutine e flussi.
Fortemente consigliato |
ViewModel interagisce con i livelli di dati o dominio utilizzando:
|
Utilizza ViewModels a livello di schermo.
Fortemente consigliato |
Non utilizzare ViewModels in elementi riutilizzabili dell'interfaccia utente. Dovresti utilizzare ViewModels in:
|
Utilizza classi standard di stato/titolare nei componenti dell'interfaccia utente riutilizzabili.
Fortemente consigliato |
Utilizza classi normali di titolari di stato 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 nel ViewModel. Puoi invece spostare la dipendenza nell'interfaccia utente o nel livello dati. |
Espone uno stato dell'interfaccia utente.
Consigliato |
I ViewModels devono esporre i dati nell'interfaccia utente tramite una singola proprietà denominata uiState . Se la UI mostra più dati non correlati, la VM può esporre più proprietà dello 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 lavorare con il software Android ciclo di vita:
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. Usa invece LifecycleObserver . Se l'app deve eseguire operazioni quando il ciclo di vita raggiunge un determinato Lifecycle.State , utilizza l'API repeatOnLifecycle . |
Il seguente snippet illustra come eseguire operazioni in base a un Stato 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) {
// ...
}
}
}
}
Compose
@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 osservare per la gestione delle dipendenze tra i componenti:
Consiglio | Descrizione |
---|---|
Utilizza l'inserimento di dipendenze.
Fortemente consigliato |
Utilizza le best practice per l'inserimento delle dipendenze, principalmente iniezione da parte del costruttore quando possibile. |
Limita l'ambito a un componente quando necessario.
Fortemente 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. |
Utilizza 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 |
---|---|
Scopri cosa testare.
Fortemente consigliato |
A meno che il progetto non sia più o meno semplice come un'app Hello World, dovresti testarlo almeno con:
|
Preferisci i falsi alle simulazioni.
Fortemente consigliato |
Scopri di più nella documentazione sull'utilizzo del test Doppio in Android. |
Testa StateFlows.
Fortemente consigliato |
Durante il test di StateFlow :
|
Per ulteriori informazioni, consulta la guida Che cosa testare nei DAC Android.
Modelli
Quando sviluppi i modelli nelle tue app, devi osservare queste best practice:
Consiglio | Descrizione |
---|---|
Creare 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 costituiti da frasi verbali. Ad esempio, makePayment() . |
Proprietà di denominazione.
Facoltativo |
Le proprietà devono essere una frase sostantivo. Ad esempio, inProgressTopicSelection . |
Denominazione dei flussi di dati.
Facoltativo |
Quando una classe espone uno stream 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>> |
Denominazioni delle interfacce.
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 OfflineFirstNewsRepository oppure InMemoryNewsRepository . Se non riesci a trovare un nome appropriato, utilizza DefaultNewsRepository .
Le implementazioni false devono essere precedute dal prefisso Fake , come in FakeAuthorsRepository . |