Recomendações para a arquitetura do Android (Views)

Conceitos e implementação do Jetpack Compose

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

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:

  • Crie repositórios, mesmo que eles contenham somente uma fonte de dados.
  • Em apps pequenos, é possível colocar tipos de camada de dados em um pacote ou módulo de data.

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

Confira mais práticas recomendadas do ViewModel.

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

Leia mais sobre repeatOnLifecycle.

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.

Recomendado

Use fragmentos de navegação para alternar entre as diferentes telas e links diretos para o app, caso ele tenha mais de uma tela.

O snippet abaixo descreve como coletar o estado da IU de uma forma que reconhece o ciclo de vida:

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

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:

  • Fluxos do Kotlin para receber dados do app.
  • Funções suspend para executar ações com viewModelScope.

Usar ViewModels na tela.

Altamente recomendável

Não use ViewModels em partes reutilizáveis da IU. Em vez disso, use-os em:

Não use AndroidViewModel.

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

Recomendado

Os ViewModels precisam expor dados à IU usando uma única propriedade conhecida como uiState. Se a UI mostrar diferentes dados não relacionados, o ViewModel poderá expor várias propriedades do estado da UI.

  • Transforme uiState em StateFlow.
  • Crie o uiState usando o operador stateIn com a política WhileSubscribed(5000) (exemplo) se os dados forem originados de outras camadas da hierarquia como um fluxo de dados.
  • Para casos mais simples sem fluxos de dados provenientes da camada de dados, é aceitável usar um MutableStateFlow exposto como um StateFlow imutável.
  • É possível optar por usar ${Screen}UiState como uma classe de dados que pode conter dados, erros e sinais de carregamento. Ela também pode ser uma classe selada, desde que os diferentes estados sejam exclusivos.

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:

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) {
                // ...
            }
        }
    }
}