Nesta página, apresentamos várias práticas recomendadas e sugestões para a Arquitetura. Adote nossas dicas para melhorar a qualidade, robustez e escalonabilidade do app. Elas também facilitam os processos de manutenção e teste.
As práticas recomendadas abaixo estão agrupadas por assunto. Um nível de importância foi atribuído a cada uma delas, de acordo com a recomendação da nossa equipe. Os níveis são os seguintes:
- Altamente recomendável: essa prática deveria ser implementada em qualquer caso que não entre em conflito direto com a sua abordagem.
- Recomendável: essa prática provavelmente vai melhorar seu app.
- Opcional: essa prática pode melhorar seu app em algumas circunstâncias.
Arquitetura com camadas
A arquitetura com camadas recomendada favorece a separação de conceitos. Ela baseia a IU em modelos de dados, segue os princípios de fonte única de verdade e de fluxo de dados unidirecional. Confira algumas práticas recomendadas para arquitetura com camadas:
Recomendação | Descrição |
---|---|
Usar uma camada de dados claramente definida.
Altamente recomendável |
A camada de dados expõe os dados do aplicativo ao restante do app e contém a grande maioria das lógicas de negócios dele.
|
Usar uma camada de IU claramente definida.
Altamente recomendável |
A camada de IU mostra os dados do app na tela e atua como o ponto principal de interação do usuário.
|
A camada de dados precisa expor os dados do app usando um repositório.
Altamente recomendável |
Componentes na camada da interface, como elementos combináveis, atividades ou ViewModels, não podem interagir diretamente com uma fonte de dados. Confira alguns exemplos de fontes de dados:
|
Usar corrotinas e fluxos.
Altamente recomendável |
Usar corrotinas e fluxos para estabelecer a comunicação entre as camadas. |
Usar uma camada de domínios.
Recomendável para apps grandes |
Use uma camada de domínios se precisar reutilizar a lógica de negócios que interage com a camada de dados em vários ViewModels ou se quiser simplificar a complexidade da lógica de negócios de um ViewModel específico. |
Camada de IU
A função da camada de IU é mostrar os dados do app na tela e atuar como o ponto principal de interação do usuário. Confira algumas práticas recomendadas para a camada de IU:
Recomendação | Descrição |
---|---|
Seguir o fluxo de dados unidirecional (UDF, na sigla em inglês).
Altamente recomendável |
Siga os princípios do fluxo de dados unidirecional (UDF), em que os ViewModels expõem o estado da interface usando o padrão de observador e recebem ações da interface por chamadas de método. |
Usar os ViewModels do AAC, caso sejam vantajosos para o app.
Altamente recomendável |
Use ViewModels do AAC para processar a lógica de negócios e busque dados do app para expor o estado da IU à IU do app (no Compose ou nas visualizações do Android).
Confira mais práticas recomendadas do ViewModel. Consulte os benefícios dos ViewModels. |
Usar a coleta de estado da IU com reconhecimento de ciclo de vida.
Altamente recomendável |
Colete o estado da interface usando o builder adequado de corrotinas com reconhecimento de ciclo de vida: repeatOnLifecycle no sistema de visualização do Android e collectAsStateWithLifecycle no Jetpack Compose.
Leia mais sobre Saiba mais sobre o |
Não enviar eventos do ViewModel para a IU.
Altamente recomendável |
Processe o evento imediatamente no ViewModel e cause uma atualização de estado com o resultado. Saiba mais sobre os eventos de IU. |
Usar um aplicativo de atividade única.
Recomendável |
Use fragmentos ou componentes de navegação para alternar entre as diferentes telas e links diretos para o app, caso ele tenha mais de uma tela. |
Usar o Jetpack Compose.
Recomendável |
Use o Jetpack Compose para criar novos apps para smartphones, tablets, dispositivos dobráveis e para Wear OS. |
O snippet abaixo descreve como coletar o estado da IU de uma forma que reconhece o ciclo de vida:
Visualizações
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
Os ViewModels são responsáveis por fornecer o estado da IU e o acesso à camada de dados. Confira algumas práticas recomendadas para ViewModels:
Recomendação | Descrição |
---|---|
Os ViewModels precisam ser independentes do ciclo de vida do Android.
Altamente recomendável |
Os ViewModels não podem conter uma referência a nenhum tipo relacionado ao ciclo de vida. Não transmita Activity, Fragment, Context ou Resources como uma dependência.
Caso um Context seja necessário no ViewModel, analise se esse elemento está na camada certa. |
Usar corrotinas e fluxos.
Altamente recomendável |
O ViewModel interage com as camadas de dados ou de domínio usando:
|
Usar ViewModels na tela.
Altamente recomendável |
Não use ViewModels em partes reutilizáveis da IU. Em vez disso, use-os em:
|
Use classes de detentores de estado simples em componentes de IU reutilizáveis.
Altamente recomendável |
Use classes de detentores de estado simples para processar a complexidade em componentes de IU reutilizáveis para que o estado possa ser elevado e controlado externamente. |
Não use o AndroidViewModel .
Recomendável |
Use a classe ViewModel , e não AndroidViewModel . A classe Application não pode ser usada no ViewModel. Em vez disso, mova a dependência para a IU ou para a camada de dados. |
Expor um estado da IU.
Recomendável |
Os ViewModels precisam expor dados à IU usando uma única propriedade conhecida como uiState . Se a IU mostrar diferentes dados não relacionados, a VM pode expor várias propriedades do estado da IU.
|
O snippet abaixo demonstra como expor o estado da IU de um 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
Apresentamos abaixo algumas práticas recomendadas para trabalhar com o ciclo de vida do Android:
Recomendação | Descrição |
---|---|
Não substitua métodos de ciclo de vida em atividades ou fragmentos.
Altamente recomendável |
Não substitua os métodos de ciclo de vida, como onResume , em atividades ou fragmentos. Em vez disso, use um LifecycleObserver . Caso o app precise executar o trabalho quando o ciclo de vida atingir um determinado Lifecycle.State , use a API repeatOnLifecycle . |
O snippet abaixo descreve como executar operações de acordo com um determinado estado do ciclo de vida:
Visualizações
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)
}
}
}
Gerenciar dependências
Existem várias práticas recomendadas que precisam ser consideradas ao gerenciar dependências entre componentes:
Recomendação | Descrição |
---|---|
Usar a injeção de dependência.
Altamente recomendável |
Quando possível, use as práticas recomendadas de injeção de dependência, principalmente a injeção de construtor. |
Definir um componente como escopo quando necessário.
Altamente recomendável |
Defina um contêiner de dependência como escopo caso o tipo contenha dados mutáveis que precisem ser compartilhados ou caso ele demande muita memória e bateria para inicializar e seja amplamente usado no app. |
Usar o Hilt.
Recomendável |
Use o Hilt ou a injeção manual de dependência em apps simples. Use o Hilt caso se trate de um projeto complexo. Por exemplo, se ele incluir:
|
Testes
Confira algumas práticas recomendadas para testes:
Recomendação | Descrição |
---|---|
Saiba o que testar.
Altamente recomendável |
A menos que seu projeto seja quase tão simples quanto um app Hello World, você precisa testá-lo usando, pelo menos:
|
Usar falsificações em vez de simulações.
Altamente recomendável |
Saiba mais na documentação Uso de testes duplos no Android. |
Testar StateFlows.
Altamente recomendável |
Ao testar StateFlow :
|
Para mais informações, consulte o guia O que testar no Android.
Modelos
Considere as práticas recomendadas abaixo ao desenvolver modelos nos seus apps:
Recomendação | Descrição |
---|---|
Criar um modelo por camada em apps complexos.
Recomendável |
Em apps complexos, crie novos modelos em camadas ou componentes diferentes quando apropriado. Confira estes exemplos:
|
Convenções de nomenclatura
Ao definir a nomenclatura da base de código, é necessário considerar estas práticas recomendadas:
Recomendação | Descrição |
---|---|
Nomear métodos.
Opcional |
Use frases verbais para nomear os métodos. Por exemplo, makePayment() . |
Nomear propriedades.
Opcional |
Use frases substantivas para nomear propriedades. Por exemplo, inProgressTopicSelection . |
Nomear fluxos de dados.
Opcional |
Quando uma classe expõe um fluxo de dados, LiveData ou qualquer outro fluxo, a convenção de nomenclatura é usar get{model}Stream() . Por exemplo, getAuthorStream(): Flow<Author> .
Se a função retornar uma lista de modelos, o nome do modelo precisa estar no plural: getAuthorsStream(): Flow<List<Author>> . |
Nomear implementações de interfaces.
Opcional |
Os nomes das implementações de interfaces precisam representar o que elas significam. Defina Default como prefixo, se não encontrar um nome mais adequado. Por exemplo, para uma interface NewsRepository , é possível usar OfflineFirstNewsRepository ou InMemoryNewsRepository . Se não encontrar um nome melhor, use DefaultNewsRepository .
Implementações falsas precisam incluir o prefixo Fake , como FakeAuthorsRepository . |