En esta página, se presentan varias recomendaciones y prácticas recomendadas para la arquitectura. Adopta estas funciones para mejorar la calidad, la solidez y la escalabilidad de tu app. También facilitan el mantenimiento y la prueba de tu app.
Las siguientes prácticas recomendadas se agrupan por tema. Cada una tiene una prioridad que refleja cuán recomendable es según el equipo. La lista de prioridades es la siguiente:
- Muy recomendada: Deberías implementar esta práctica, a menos que entre en conflicto con tu estrategia.
- Recomendada: Es probable que esta práctica mejore tu app.
- Opcional: Esta práctica puede mejorar tu app en determinadas circunstancias.
Arquitectura en capas
En la arquitectura en capas que recomendamos, se prefiere la separación de problemas. Controla la IU a partir de modelos de datos, cumple con el principio de fuente de confianza única y sigue los principios del flujo unidireccional de datos. Estas son algunas prácticas recomendadas para la arquitectura en capas:
Recomendación | Descripción |
---|---|
Usa una capa de datos claramente definida.
Muy recomendada |
La capa de datos expone los datos de la aplicación al resto de la app y contiene la gran mayoría de su lógica empresarial.
|
Usa una capa de la IU claramente definida.
Muy recomendada |
La capa de la IU muestra los datos de la aplicación en la pantalla y sirve como punto principal de la interacción con el usuario.
|
La capa de datos debería exponer los datos de la aplicación mediante un repositorio.
Muy recomendada |
Los componentes de la capa de la IU, como los elementos componibles, las actividades o los ViewModels, no deben interactuar de forma directa con una fuente de datos. Estos son algunos ejemplos de fuentes de datos:
|
Usa corrutinas y flujos.
Muy recomendada |
Usa corrutinas y flujos para establecer la comunicación entre capas.
Aquí encontrarás más prácticas recomendadas sobre corrutinas. |
Usa una capa de dominio.
Recomendada en apps grandes |
Usa una capa de dominio, casos de uso, si necesitas reutilizar la lógica empresarial que interactúa con la capa de datos en varios ViewModels o si deseas simplificar la complejidad de la lógica empresarial de un ViewModel en particular. |
Capa de la IU
La función de la capa de la IU es mostrar los datos de la aplicación en la pantalla y servir como punto principal de interacción con el usuario. Estas son algunas prácticas recomendadas para la capa de la IU:
Recomendación | Descripción |
---|---|
Sigue el Flujo unidireccional de datos.
Muy recomendada |
Sigue los principios del flujo unidireccional de datos, en el que los ViewModels exponen el estado de la IU usando el patrón del observador y reciben acciones de la IU a través de llamadas de método. |
Usa AAC de ViewModels si sus beneficios se aplican a tu app.
Muy recomendada |
Usa AAC de ViewModels para controlar la lógica empresarial y recuperar datos de la aplicación a fin de exponer el estado de la IU a la IU (Compose o vistas de Android).
Consulta más prácticas recomendadas para ViewModel aquí. Consulta los beneficios de ViewModels aquí. |
Usa la recopilación de estado de la IU optimizada para ciclos de vida.
Muy recomendada |
Recopila el estado de la IU con el compilador de corrutinas optimizado para ciclos de vida: repeatOnLifecycle en el sistema de vistas y collectAsStateWithLifecycle en Jetpack Compose.Obtén más información sobre Obtén más información sobre |
No envíes eventos del ViewModel a la IU.
Muy recomendada |
Procesa el evento inmediatamente en ViewModel y genera una actualización de estado con el resultado del control del evento. Obtén más información sobre los eventos de IU aquí. |
Usa una aplicación de una sola actividad.
Recomendada |
Usa Navigation Fragments o Navigation Compose para navegar entre pantallas y establece un vínculo directo a tu app si esta tiene más de una. |
Usa Jetpack Compose.
Recomendada |
Usa Jetpack Compose para compilar apps nuevas para teléfonos, tablets, plegables y dispositivos Wear OS. |
En el siguiente fragmento, se describe cómo recopilar el estado de la IU de manera optimizada para los ciclos de vida:
Vistas
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
Los ViewModels son responsables de proporcionar el estado de la IU y el acceso a la capa de datos. Estas son algunas prácticas recomendadas para ViewModels:
Recomendación | Descripción |
---|---|
Los ViewModels deben ser independientes del ciclo de vida de Android.
Muy recomendada |
Los ViewModels no deben contener referencias a ningún tipo relacionado con el ciclo de vida. No pases Activity, Fragment, Context ni Resources como dependencia.
Si algo necesita un Context en el ViewModel, debes evaluar con atención si está en la capa correcta. |
Usa corrutinas y flujos.
Muy recomendada |
ViewModel interactúa con los datos o las capas de dominio mediante lo siguiente:
|
Usa ViewModels a nivel de la pantalla.
Muy recomendada |
No uses ViewModels en piezas de IU reutilizables. Debes usar ViewModels en lo siguiente:
|
Usa clases contenedoras de estados sin formato en componentes de IU reutilizables.
Muy recomendada |
Usa clases contenedoras de estados sin formato para controlar la complejidad en componentes de IU reutilizables. Cuando haces esto, el estado se puede elevar y controlar de forma externa. |
No uses AndroidViewModel .
Recomendada |
Usa la clase ViewModel , no AndroidViewModel . No se debe usar la clase Application en ViewModel. En su lugar, mueve la dependencia a la IU o a la capa de datos. |
Expón un estado de IU.
Recomendada |
Los ViewModels deben exponer datos a la IU a través de una única propiedad llamada uiState . Si la IU muestra varios datos no relacionados, la VM puede exponer varias propiedades del estado de la IU.
|
En el siguiente fragmento, se describe cómo exponer el estado de la IU desde 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 de vida
Las siguientes son algunas recomendaciones para trabajar con el ciclo de vida de Android:
Recomendación | Descripción |
---|---|
No anules los métodos de ciclo de vida de objetos Activity ni Fragment.
Muy recomendada |
No anules métodos del ciclo de vida, como onResume , en actividades o fragmentos. En su lugar, usa LifecycleObserver . Si la app necesita realizar un trabajo cuando el ciclo de vida alcanza un cierto Lifecycle.State , usa la API de repeatOnLifecycle . |
En el siguiente fragmento, se describe cómo realizar operaciones en función de un determinado estado de ciclo de vida:
Vistas
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)
}
}
}
Control de dependencias
Hay varias prácticas recomendadas que debes tener en cuenta cuando administras dependencias entre componentes:
Recomendación | Descripción |
---|---|
Usa la inserción de dependencias.
Muy recomendada |
Usa las prácticas recomendadas de inserción de dependencias, en especial, la inserción del constructor, cuando sea posible. |
Define el alcance para un componente cuando sea necesario.
Muy recomendada |
Define el alcance para un contenedor de dependencias cuando el tipo contenga datos mutables que se deban compartir o resulte costoso inicializar el tipo y se use mucho en la app. |
Usa Hilt.
Recomendada |
Usa Hilt o la inserción manual de dependencias en apps simples. Usa Hilt si tu proyecto es lo suficientemente complejo. Por ejemplo, si tienes lo siguiente:
|
Pruebas
Estas son algunas recomendaciones para las pruebas:
Recomendación | Descripción |
---|---|
Comprende qué probar.
Muy recomendada |
A menos que el proyecto sea tan simple como la app de Hello World, deberías realizar pruebas, como mínimo, con lo siguiente:
|
Opta por simulaciones en lugar de muestras.
Muy recomendada |
Obtén más información en la documentación para usar dobles de prueba en Android. |
Prueba StateFlows.
Muy recomendada |
Cuando pruebes StateFlow , haz lo siguiente:
|
Para obtener más información, consulta la guía Qué probar en Android DAC.
Modelos
Debes tener en cuenta las siguientes prácticas recomendadas a la hora de desarrollar modelos en tus apps:
Recomendación | Descripción |
---|---|
Crea un modelo por capa en apps complejas.
Recomendada |
En apps complejas, crea modelos nuevos en diferentes capas o componentes cuando hacerlo tenga sentido. Considera los siguientes ejemplos:
|
Convenciones de nombres
A la hora de nombrar tu base de código, debes tener en cuenta las siguientes prácticas recomendadas:
Recomendación | Descripción |
---|---|
Nombra métodos.
Opcional |
Los métodos deberían constar de una frase verbal. Por ejemplo, makePayment() . |
Nombra propiedades.
Opcional |
Las propiedades deberían constar de una frase nominal. Por ejemplo, inProgressTopicSelection . |
Nombra flujos de datos.
Opcional |
Cuando una clase expone una transmisión de flujo, de LiveData o cualquier otra, la convención de nombres es get{model}Stream() . Por ejemplo, getAuthorStream(): Flow<Author> .
Si la función muestra una lista de modelos, el nombre del modelo debería estar en plural: getAuthorsStream(): Flow<List<Author>> . |
Nombra las implementaciones de interfaces.
Opcional |
Los nombres de las implementaciones de interfaces deberían ser significativos. Usa Default como prefijo si no se encuentra un nombre mejor. Por ejemplo, para una interfaz NewsRepository , puedes tener un OfflineFirstNewsRepository o InMemoryNewsRepository . Si no encuentras ningún nombre apropiado, usa DefaultNewsRepository .
Las implementaciones simuladas deben contener el prefijo Fake , como en FakeAuthorsRepository . |