Cette page présente plusieurs bonnes pratiques et recommandations concernant l'architecture. Adoptez-les pour améliorer la qualité, la robustesse et l'évolutivité de votre application. Elles facilitent également la gestion et le test de votre application.
Les bonnes pratiques ci-dessous sont regroupées par thème. Chacune d'elles a une priorité indiquant à quel point l'équipe la recommande. Voici la liste des priorités :
- Fortement recommandé : vous devriez appliquer cette pratique, sauf si elle entre fondamentalement en conflit avec votre approche.
- Recommandé : il est probable que cette pratique améliore votre application.
- Facultatif : cette pratique peut améliorer votre application dans certains cas.
Architecture multicouche
Notre architecture multicouche recommandée favorise la séparation des tâches. Elle pilote l'interface utilisateur à partir de modèles de données, est conforme au principe de référence unique et suit les principes du flux de données unidirectionnel. Voici quelques bonnes pratiques pour l'architecture multicouche :
Recommandation | Description |
---|---|
Utilisez une couche de données clairement définie.
Fortement recommandé |
La couche de données expose les données de l'application au reste de l'application et contient la grande majorité de la logique métier de votre application.
|
Utilisez une couche d'interface utilisateur bien définie.
Fortement recommandé |
La couche d'interface utilisateur affiche les données de l'application à l'écran et sert de point principal d'interaction utilisateur.
|
La couche de données doit exposer les données d'application à l'aide d'un dépôt.
Fortement recommandé |
Les composants de la couche d'interface utilisateur tels que les composables, les activités ou les ViewModels ne doivent pas interagir directement avec une source de données. Exemples de sources de données :
|
Utilisez des coroutines et des flux.
Fortement recommandé |
Utilisez des coroutines et des flux pour communiquer entre les couches. |
Utilisez une couche de domaine.
Recommandé pour les applications volumineuses |
Utilisez une couche de domaine si vous devez réutiliser la logique métier qui interagit avec la couche de données sur plusieurs ViewModels, ou si vous souhaitez simplifier la logique métier d'un ViewModel particulier. |
Couche d'interface utilisateur
Le rôle de la couche d'interface utilisateur est d'afficher les données de l'application à l'écran et de servir de point principal d'interaction utilisateur. Voici quelques bonnes pratiques pour la couche d'interface utilisateur :
Recommandation | Description |
---|---|
Suivez les principes du flux de données unidirectionnel (UDF).
Fortement recommandé |
Suivez les principes du flux de données unidirectionnel (UDF), selon lesquels les ViewModels exposent l'état de l'UI à l'aide du modèle d'observateur et reçoivent les actions de l'UI via des appels de méthode. |
Utilisez des ViewModels AAC si cela peut être utile pour votre application.
Fortement recommandé |
Utilisez des ViewModels AAC pour gérer la logique métier et récupérer les données de l'application pour exposer l'état de l'UI à l'UI (Compose ou Android Views). |
Collectez l'état de l'UI en tenant compte du cycle de vie.
Fortement recommandé |
Collectez l'état de l'interface utilisateur depuis celle-ci à l'aide du compilateur de coroutines qui tient compte du cycle de vie : repeatOnLifecycle dans le système View et collectAsStateWithLifecycle dans Jetpack Compose.
En savoir plus sur En savoir plus sur |
N'envoyez pas d'événements du ViewModel à l'UI.
Fortement recommandé |
Traitez l'événement immédiatement dans ViewModel et mettez à jour l'état avec le résultat de la gestion de l'événement. En savoir plus sur les événements d'interface utilisateur |
Utilisez une application à activité unique.
Recommandé |
Utilisez Navigation Fragments ou Navigation Compose pour naviguer entre les écrans, et utilisez des liens profonds vers votre application si elle comporte plusieurs écrans. |
Utilisez Jetpack Compose.
Recommandé |
Utilisez Jetpack Compose pour créer des applications pour téléphones, tablettes, appareils pliables et Wear OS. |
L'extrait de code suivant montre comment collecter l'état de l'interface utilisateur en tenant compte du cycle de vie :
Vues
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
Les ViewModels fournissent l'état de l'interface utilisateur et l'accès à la couche de données. Voici quelques bonnes pratiques pour les ViewModels :
Recommandation | Description |
---|---|
Les ViewModels doivent être indépendants du cycle de vie d'Android.
Fortement recommandé |
Les ViewModels ne doivent faire référence à aucun type lié au cycle de vie. Ne transmettez pas d'Activity, Fragment, Context ni de Resources en tant que dépendance.
Si un élément a besoin d'un Context dans le ViewModel, vous devez sérieusement vous demander s'il se trouve dans la bonne couche. |
Utilisez des coroutines et des flux.
Fortement recommandé |
Le ViewModel interagit avec les couches de données ou de domaine à l'aide des éléments suivants :
|
Utilisez les ViewModels au niveau de l'écran.
Fortement recommandé |
N'utilisez pas de ViewModels dans les éléments réutilisables de l'UI. Vous devez utiliser les ViewModels dans les éléments suivants :
|
Utilisez des classes de conteneurs d'état simples dans les composants d'UI réutilisables.
Fortement recommandé |
Utilisez des classes de conteneurs d'état simples pour gérer la complexité des composants d'UI réutilisables. Cela permet de hisser et de contrôler l'état en externe. |
N'utilisez pas AndroidViewModel .
Recommandé |
Utilisez la classe ViewModel , pas AndroidViewModel . La classe Application ne doit pas être utilisée dans le ViewModel. Déplacez plutôt la dépendance vers l'UI ou la couche de données. |
Exposez un état de l'interface utilisateur.
Recommandé |
Les ViewModels doivent exposer les données à l'UI via une seule propriété appelée uiState . Si l'interface utilisateur affiche plusieurs données sans liens entre elles, le ViewModel peut exposer plusieurs propriétés d'état de l'interface utilisateur.
|
L'extrait de code suivant montre comment exposer l'état de l'interface utilisateur à partir d'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
)
// ...
}
Cycle de vie
Voici quelques bonnes pratiques pour le cycle de vie Android :
Recommandation | Description |
---|---|
N'ignorez pas les méthodes du cycle de vie dans les activités ou les fragments.
Fortement recommandé |
N'ignorez pas les méthodes du cycle de vie comme onResume dans les activités ou les fragments. Utilisez LifecycleObserver à la place. Si l'application doit effectuer des tâches lorsque le cycle de vie atteint un certain Lifecycle.State , utilisez l'API repeatOnLifecycle . |
L'extrait de code suivant montre comment effectuer des opérations en fonction d'un certain état du cycle de vie :
Vues
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)
}
}
}
Gérer les dépendances
Il existe plusieurs bonnes pratiques à suivre lorsque vous gérez des dépendances entre des composants :
Recommandation | Description |
---|---|
Utilisez l'injection de dépendances.
Fortement recommandé |
Dans la mesure du possible, suivez les bonnes pratiques concernant l'injection de dépendances, en particulier l'injection de constructeur. |
Si nécessaire, limitez la portée à un composant.
Fortement recommandé |
Limitez la portée à un conteneur de dépendances lorsque le type contient des données modifiables devant être partagées, ou que son initialisation est coûteuse et qu'il est largement utilisé dans l'application. |
Utilisez Hilt.
Recommandé |
Utilisez Hilt ou une injection de dépendances manuelle dans les applications simples. Utilisez Hilt si votre projet est suffisamment complexe. Par exemple, dans les cas suivants :
|
Tests
Voici quelques bonnes pratiques pour les tests :
Recommandation | Description |
---|---|
Identifiez les éléments à tester.
Fortement recommandé |
À moins que le projet ne soit à peu près aussi simple qu'une application Hello World, vous devez le tester, au minimum avec :
|
Préférez les faux aux simulations.
Fortement recommandé |
Pour en savoir plus, consultez Utiliser les doubles de test dans la documentation Android. |
Testez les StateFlows.
Fortement recommandé |
Lorsque vous testez StateFlow :
|
Pour en savoir plus, consultez le guide Éléments à tester dans Android DAC.
Modèles
Appliquez ces bonnes pratiques lorsque vous développez des modèles dans vos applications :
Recommandation | Description |
---|---|
Créez un modèle par couche dans les applications complexes.
Recommandé |
Dans les applications complexes, créez des modèles dans différentes couches ou différents composants lorsque cela est pertinent. Exemples :
|
Conventions d'attribution de noms
Lorsque vous nommez votre codebase, vous devez tenir compte des bonnes pratiques suivantes :
Recommandation | Description |
---|---|
Nom des méthodes.
Facultatif |
Le nom des méthodes doit être un groupe verbal. Par exemple, makePayment() . |
Nom des propriétés.
Facultatif |
Le nom des propriétés doit être un groupe nominal. Par exemple, inProgressTopicSelection . |
Nom des flux de données.
Facultatif |
Lorsqu'une classe expose un flux Flow, LiveData ou autre, la convention d'attribution de noms est get{model}Stream() . Par exemple, getAuthorStream(): Flow<Author> .
Si la fonction renvoie une liste de modèles, le nom du modèle doit être au pluriel : getAuthorsStream(): Flow<List<Author>> |
Nom des implémentations d'interface.
Facultatif |
Les noms des implémentations d'interface doivent être explicites. Utilisez Default comme préfixe si vous ne trouvez aucun nom plus adapté. Par exemple, pour une interface NewsRepository , vous pouvez utiliser OfflineFirstNewsRepository ou InMemoryNewsRepository . Si vous ne trouvez pas de nom approprié, utilisez DefaultNewsRepository .
Les fausses implémentations doivent être précédées de Fake , par exemple FakeAuthorsRepository . |