Visão geral do ViewModel   Parte do Android Jetpack.

A classe ViewModel foi projetada para armazenar e gerenciar dados relacionados à IU considerando o ciclo de vida. A classe ViewModel permite que os dados sobrevivam às mudanças de configuração, como a rotação da tela.

O framework do Android gerencia o ciclo de vida dos controladores de IU, como atividades e fragmentos. Ele pode decidir destruir ou recriar um controlador de IU em resposta a determinadas ações do usuário ou eventos do dispositivo que estão completamente fora do seu controle.

Se o sistema destruir ou recriar um controlador de IU, todos os dados temporários relacionados à IU armazenados nele serão perdidos. Por exemplo, seu app pode incluir uma lista de usuários em uma das atividades dele. Quando a atividade é recriada para uma alteração de configuração, a nova atividade precisa buscar novamente a lista de usuários. Para dados simples, a atividade pode usar o método onSaveInstanceState() e restaurar os próprios dados a partir do pacote em onCreate(), mas essa abordagem é adequada apenas para pequenos volumes de dados que podem ser serializados e depois desserializados, não para volumes potencialmente grandes de dados, como uma lista de usuários ou bitmaps.

Outro problema é que os controladores de IU geralmente precisam fazer chamadas assíncronas que podem levar algum tempo para retornar. O controlador de IU precisa gerenciar essas chamadas e garantir que o sistema as limpe depois de ser destruído, para evitar possíveis vazamentos de memória. Esse gerenciamento requer muita manutenção e, caso o objeto seja recriado para uma alteração de configuração, representa um desperdício de recursos, porque o objeto pode precisar emitir novamente as chamadas já feitas.

Os controladores de IU, como atividades e fragmentos, têm como objetivo principal exibir dados da IU, reagir às ações do usuário ou lidar com tarefas de comunicação com o sistema operacional, como solicitações de permissão. A exigência de que os controladores de IU também sejam responsáveis pelo carregamento de dados de um banco de dados ou de uma rede acaba tornando a classe pesada. Atribuir responsabilidades excessivas aos controladores de IU pode fazer com que uma única classe precise lidar sozinha com todo o trabalho de um app, em vez de delegar o trabalho para outras classes. Atribuir responsabilidades excessivas aos controladores de IU dessa maneira também dificulta muito os testes.

É mais fácil e eficiente manter a propriedade de dados de visualização separada da lógica do controlador de IU.

Implementar um ViewModel

Os componentes de arquitetura fornecem a classe auxiliar ViewModel para o controlador de IU responsável por preparar dados para a IU. Objetos ViewModel são retidos automaticamente durante as mudanças de configuração, de modo que os dados retidos estejam imediatamente disponíveis para a próxima atividade ou instância de fragmento. Por exemplo, se você precisar mostrar uma lista de usuários no app, atribua a responsabilidade de adquirir e manter a lista de usuários a um ViewModel, em vez de uma atividade ou fragmento, como mostrado no exemplo de código abaixo:

Visualizações

class MyViewModel : ViewModel() {
    private val users: MutableLiveData<List<User>> by lazy {
        MutableLiveData<List<User>>().also {
            loadUsers()
        }
    }

    fun getUsers(): LiveData<List<User>> {
        return users
    }

    private fun loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

Visualizações

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<User>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

Depois, você pode acessar a lista de uma atividade desta maneira:

Visualizações

class MyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        // Use the 'by viewModels()' Kotlin property delegate
        // from the activity-ktx artifact
        val model: MyViewModel by viewModels()
        model.getUsers().observe(this, Observer<List<User>>{ users ->
            // update UI
        })
    }
}

Visualizações

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

Se a atividade for recriada, ela vai receber a mesma instância MyViewModel que foi criada pela primeira atividade. Quando a atividade do proprietário está concluída, o framework chama o método onCleared() do objeto ViewModel para que ele possa limpar os recursos.

Objetos ViewModel são projetados para sobreviver aos instanciamentos específicos de visualizações ou LifecycleOwners. Esse design também significa que é possível escrever testes para cobrir um ViewModel mais facilmente, porque ele não conhece os objetos de visualização e objetos Lifecycle. Objetos ViewModel podem conter LifecycleObservers, como objetos LiveData. No entanto, os objetos ViewModel nunca podem observar mudanças nos observáveis com reconhecimento de ciclo de vida, como os objetos LiveData. Se o ViewModel precisa do contexto de Application, por exemplo, para localizar um serviço do sistema, ele pode estender a classe AndroidViewModel e usar um construtor que receba o Application, já que a classe Application estende o Context.

Criar ViewModels com dependências

Seguindo as práticas recomendadas de injeção de dependência, os ViewModels podem usar dependências como parâmetros no construtor. Elas são principalmente de tipos das camadas de domínio ou de dados. Como o framework fornece os ViewModels, é necessário um mecanismo especial para criar instâncias deles. Esse mecanismo é a interface ViewModelProvider.Factory. Somente implementações dessa interface podem instanciar ViewModels no escopo correto.

Se uma classe do ViewModel receber dependências no construtor, forneça uma fábrica que implemente a interface ViewModelProvider.Factory. Substitua a função create(Class<T>, CreationExtras) para fornecer uma nova instância do ViewModel.

O elemento CreationExtras permite que você acesse informações relevantes que ajudam a instanciar um ViewModel. Confira uma lista de chaves que podem ser acessadas nos extras:

Chave Funcionalidade
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY Esta Key fornece acesso à chave personalizada que você transmitiu para ViewModelProvider.get().
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY Fornece acesso à instância da classe Application.
SavedStateHandleSupport.DEFAULT_ARGS_KEY Fornece acesso ao pacote de argumentos que você precisa usar para construir um SavedStateHandle.
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY Fornece acesso ao SavedStateRegistryOwner que está sendo usado para construir o ViewModel.
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY Fornece acesso ao ViewModelStoreOwner que está sendo usado para construir o ViewModel.

Para criar uma nova instância de SavedStateHandle, use a função CreationExtras.createSavedStateHandle().createSavedStateHandle()) e a transmita para o ViewModel.

Confira abaixo um exemplo de como fornecer uma instância de um ViewModel que usa um repositório com escopo para a classe Application e SavedStateHandle como dependências:

Visualizações

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.CreationExtras

class MyViewModel(
    private val myRepository: MyRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    // ViewModel logic
    // ...

    // Define ViewModel factory in a companion object
    companion object {

        val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory {
            @Suppress("UNCHECKED_CAST")
            override fun <T : ViewModel> create(
                modelClass: Class<T>,
                extras: CreationExtras
            ): T {
                // Get the Application object from extras
                val application = checkNotNull(extras[APPLICATION_KEY])
                // Create a SavedStateHandle for this ViewModel from extras
                val savedStateHandle = extras.createSavedStateHandle()

                return MyViewModel(
                    (application as MyApplication).myRepository,
                    savedStateHandle
                ) as T
            }
        }
    }
}

Visualizações

import static androidx.lifecycle.SavedStateHandleSupport.createSavedStateHandle;
import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY;

import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.viewmodel.ViewModelInitializer;

public class MyViewModel extends ViewModel {

    public MyViewModel(
        MyRepository myRepository,
        SavedStateHandle savedStateHandle
    ) { /* Init ViewModel here */ }

    static final ViewModelInitializer<MyViewModel> initializer = new ViewModelInitializer<>(
        MyViewModel.class,
        creationExtras -> {
            MyApplication app = (MyApplication) creationExtras.get(APPLICATION_KEY);
            assert app != null;
            SavedStateHandle savedStateHandle = createSavedStateHandle(creationExtras);

            return new MyViewModel(app.getMyRepository(), savedStateHandle);
        }
    );
}

Em seguida, use essa fábrica ao extrair uma instância do ViewModel:

Visualizações

import androidx.activity.viewModels

class MyActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by viewModels { MyViewModel.Factory }

    // Rest of Activity code
}

Visualizações

import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;

public class MyActivity extends AppCompatActivity {

    MyViewModel myViewModel = new ViewModelProvider(
        this,
        ViewModelProvider.Factory.from(MyViewModel.initializer)
    ).get(MyViewModel.class);

    // Rest of Activity code
}

Compose

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyScreen(
    modifier: Modifier = Modifier,
    viewModel: MyViewModel = viewModel(factory = MyViewModel.Factory)
) {
    // ...
}

Como alternativa, use a DSL de fábrica do ViewModel para criar fábricas que usam uma API Kotlin mais idiomática:

Visualizações

import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
import androidx.lifecycle.createSavedStateHandle
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory

class MyViewModel(
    private val myRepository: MyRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    // ViewModel logic

    // Define ViewModel factory in a companion object
    companion object {
        val Factory: ViewModelProvider.Factory = viewModelFactory {
            initializer {
                val savedStateHandle = createSavedStateHandle()
                val myRepository = (this[APPLICATION_KEY] as MyApplication).myRepository
                MyViewModel(
                    myRepository = myRepository,
                    savedStateHandle = savedStateHandle
                )
            }
        }
    }
}

Fábricas para versões do ViewModel anteriores à 2.5.0

Se você estiver usando uma versão do ViewModel anterior à 2.5.0, vai precisar fornecer fábricas de um subconjunto de classes que estendem ViewModelProvider.Factory e implementar a função create(Class<T>). De acordo com as dependências necessárias para o ViewModel, uma classe diferente precisa ser estendida de:

Se os elementos Application e SavedStateHandle não forem necessários, basta estender a ViewModelProvider.Factory.

O exemplo abaixo usa uma AbstractSavedStateViewModelFactory para um ViewModel, que usa um repositório e um tipo SavedStateHandle como dependência.

Visualizações

class MyViewModel(
    private val myRepository: MyRepository,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {

    // ViewModel logic ...

    // Define ViewModel factory in a companion object
    companion object {
        fun provideFactory(
            myRepository: MyRepository,
            owner: SavedStateRegistryOwner,
            defaultArgs: Bundle? = null,
        ): AbstractSavedStateViewModelFactory =
            object : AbstractSavedStateViewModelFactory(owner, defaultArgs) {
                @Suppress("UNCHECKED_CAST")
                override fun <T : ViewModel> create(
                    key: String,
                    modelClass: Class<T>,
                    handle: SavedStateHandle
                ): T {
                    return MyViewModel(myRepository, handle) as T
                }
            }
    }
}

Visualizações

import androidx.annotation.NonNull;
import androidx.lifecycle.AbstractSavedStateViewModelFactory;
import androidx.lifecycle.SavedStateHandle;
import androidx.lifecycle.ViewModel;

public class MyViewModel extends ViewModel {
    public MyViewModel(
        MyRepository myRepository,
        SavedStateHandle savedStateHandle
    ) { /* Init ViewModel here */ }
}

public class MyViewModelFactory extends AbstractSavedStateViewModelFactory {

    private final MyRepository myRepository;

    public MyViewModelFactory(
        MyRepository myRepository
    ) {
        this.myRepository = myRepository;
    }

    @SuppressWarnings("unchecked")
    @NonNull
    @Override
    protected <T extends ViewModel> T create(
        @NonNull String key, @NonNull Class<T> modelClass, @NonNull SavedStateHandle handle
    ) {
        return (T) new MyViewModel(myRepository, handle);
    }
}

Em seguida, use a fábrica para extrair o ViewModel:

Visualizações

import androidx.activity.viewModels

class MyActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by viewModels {
        MyViewModel.provideFactory((application as MyApplication).myRepository, this)
    }

    // Rest of Activity code
}

Visualizações

public class MyActivity extends AppCompatActivity {

    MyViewModel myViewModel = new ViewModelProvider(
        this,
        new MyViewModelFactory(((MyApplication) getApplication()).getMyRepository())
    ).get(MyViewModel.class);

    // Rest of Activity code
}

Compose

import androidx.lifecycle.viewmodel.compose.viewModel

@Composable
fun MyScreen(
    modifier: Modifier = Modifier,
    viewModel: MyViewModel = viewModel(
        factory = MyViewModel.provideFactory(
            (LocalContext.current.applicationContext as MyApplication).myRepository,
            owner = LocalSavedStateRegistryOwner.current
        )
    )
) {
    // ...
}

O ciclo de vida de um ViewModel

Objetos ViewModel têm como escopo o Lifecycle do ViewModelStoreOwner transferido para o ViewModelProvider ao receber o ViewModel. O ViewModel permanece na memória até o ViewModelStoreOwner referente ao escopo dele desaparecer permanentemente:

  • No caso de uma atividade, quando ela é finalizada.
  • No caso de um fragmento, quando ele é desanexado.
  • No caso de uma entrada de navegação, quando ela é removida da backstack.

Isso faz com que os ViewModels sejam uma ótima solução para armazenar dados que resistam a mudanças de configuração.

A Figura 1 ilustra os vários estados do ciclo de vida de uma atividade à medida que ela é submetida a uma rotação e, em seguida, é concluída. A ilustração também mostra a vida útil do ViewModel ao lado do ciclo de vida da atividade associada. Este diagrama específico ilustra os estados de uma atividade. Os mesmos estados básicos se aplicam ao ciclo de vida de um fragmento.

Ilustra o ciclo de vida de um ViewModel como um estado de mudanças de atividade.

Geralmente, você solicita um ViewModel na primeira vez que o sistema chama o método onCreate() de um objeto de atividade. O sistema pode chamar onCreate() várias vezes durante a vida de uma atividade, como quando a tela de um dispositivo é rotacionada. O ViewModel existe a partir do momento em que um ViewModel é solicitado pela primeira vez até que a atividade seja finalizada e destruída.

APIs do ViewModel

O método ViewModelProvider.get() permite acessar uma instância de um ViewModel com escopo para qualquer ViewModelStoreOwner. Para usuários do Kotlin, há diferentes funções de extensão disponíveis para os casos de uso mais comuns. Todas as implementações de função de extensão do Kotlin usam a API ViewModelProvider internamente.

ViewModels com escopo para o ViewModelStoreOwner mais próximo

Você pode definir o escopo de um ViewModel para uma atividade, um fragmento ou um destino de um gráfico de navegação. As funções de extensão viewModels() fornecidas pelas bibliotecas Activity, Fragment e Navigation e a função viewModel() no Compose permitem que você tenha uma instância do ViewModel com escopo o mais próximo de ViewModelStoreOwner.

Visualizações

class MyActivity : AppCompatActivity() {

    // ViewModel API available in activity.activity-ktx
    // The ViewModel is scoped to `this` Activity
    val viewModel: MyViewModel by viewModels()
}

class MyFragment : Fragment() {

    // ViewModel API available in fragment.fragment-ktx
    // The ViewModel is scoped to `this` Fragment
    val viewModel: MyViewModel by viewModels()
}

Visualizações

public class MyActivity extends AppCompatActivity {

    // The ViewModel is scoped to `this` Activity
    MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
}

public class MyFragment extends Fragment {

    // The ViewModel is scoped to `this` Fragment
    MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
}

Compose

@Composable
fun MyScreen(
    modifier: Modifier = Modifier,
    // ViewModel API available in lifecycle.lifecycle-viewmodel-compose
    // The ViewModel is scoped to the closest ViewModelStoreOwner provided
    // via the LocalViewModelStoreOwner CompositionLocal. This could be the
    // host Activity or Fragment, or destination of the Navigation graph.
    viewModel: MyViewModel = viewModel()
) { /* ... */ }

ViewModels com escopo para qualquer ViewModelStoreOwner

As funções ComponentActivity.viewModels() e Fragment.viewModels() no sistema de visualização e a função viewModel() no Compose usam um parâmetro ownerProducer opcional que pode ser usado para especificar a qual ViewModelStoreOwner vai o escopo da instância do ViewModel. O exemplo a seguir mostra como receber uma instância de um ViewModel com escopo para o fragmento pai.

Visualizações

class MyFragment : Fragment() {

    // ViewModel API available in fragment.fragment-ktx
    // The ViewModel is scoped to the parent of `this` Fragment
    val viewModel: SharedViewModel by viewModels(
        ownerProducer = { requireParentFragment() }
    )
}

Visualizações

public class MyFragment extends Fragment {

    SharedViewModel viewModel;

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        // The ViewModel is scoped to the parent of `this` Fragment
        viewModel = new ViewModelProvider(requireParentFragment())
            .get(SharedViewModel.class);
    }
}

Compose

@Composable
fun MyScreen(
    context: Context = LocalContext.current,
    // ViewModel API available in lifecycle.lifecycle-viewmodel-compose
    // The ViewModel is scoped to the parent of the host Fragment
    // where this composable function is called
    viewModel: SharedViewModel = viewModel(
        viewModelStoreOwner = (context as Fragment).requireParentFragment()
    )
) { /* ... */ }

Receber um ViewModel com escopo de atividade de um fragmento pode ser um caso de uso comum no seu app. Por isso, a função de extensão activityViewModels() da visualização está disponível. Se você não estiver usando Visualizações e Kotlin, pode usar as mesmas APIs acima e transmitir o proprietário correto.

Visualizações

class MyFragment : Fragment() {

    // ViewModel API available in fragment.fragment-ktx
    // The ViewModel is scoped to the host Activity
    val viewModel: SharedViewModel by activityViewModels()
}

Visualizações

public class MyFragment extends Fragment {

    SharedViewModel viewModel;

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        // The ViewModel is scoped to the host Activity
        viewModel = new ViewModelProvider(requireActivity())
            .get(SharedViewModel.class);
    }
}

Compose

@Composable
fun MyScreen(
    context: Context = LocalContext.current,
    // ViewModel API available in lifecycle.lifecycle-viewmodel-compose
    // The ViewModel is scoped to the Activity of the host Fragment
    // where this composable function is called
    viewModel: SharedViewModel = viewModel(
        viewModelStoreOwner = (context as Fragment).requireActivity()
    )
) { /* ... */ }

ViewModels com escopo para o gráfico de navegação

Os gráficos de navegação também são proprietários de armazenamento do ViewModel. Se você estiver usando o fragmento de navegação ou o Navigation Compose, pode ver uma instância de um ViewModel com escopo para um gráfico de navegação com a função de extensão navGraphViewModels(graphId) de Visualizações.

Visualizações

class MyFragment : Fragment() {

    // ViewModel API available in navigation.navigation-fragment
    // The ViewModel is scoped to the `nav_graph` Navigation graph
    val viewModel: SharedViewModel by navGraphViewModels(R.id.nav_graph)

    // Equivalent navGraphViewModels code using the viewModels API
    val viewModel: SharedViewModel by viewModels(
        { findNavController().getBackStackEntry(R.id.nav_graph) }
    )
}

Visualizações

public class MyFragment extends Fragment {

    SharedViewModel viewModel;

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        NavController navController = NavHostFragment.findNavController(this);
        NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph);

        // The ViewModel is scoped to the `nav_graph` Navigation graph
        viewModel = new ViewModelProvider(backStackEntry).get(SharedViewModel.class);
    }
}

Compose

@Composable
fun MyAppNavHost() {
    // ...
    composable("myScreen") { backStackEntry ->
        val parentEntry = remember(backStackEntry) {
            navController.getBackStackEntry("parentNavigationRoute")
        }
        // The ViewModel is scoped to the `parentNavigationRoute` Navigation graph
        val parentViewModel: SharedViewModel = viewModel(parentEntry)
        // ...
    }
}

Se você estiver usando o Hilt junto da navegação do Jetpack, é possível usar a API hiltNavGraphViewModels(graphId) da seguinte maneira:

Visualizações

class MyFragment : Fragment() {

    // ViewModel API available in hilt.hilt-navigation-fragment
    // The ViewModel is scoped to the `nav_graph` Navigation graph
    // and is provided using the Hilt-generated ViewModel factory
    val viewModel: SharedViewModel by hiltNavGraphViewModels(R.id.nav_graph)
}

Visualizações

public class MyFragment extends Fragment {

    SharedViewModel viewModel;

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        NavController navController = NavHostFragment.findNavController(this);
        NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph);

        // The ViewModel is scoped to the `nav_graph` Navigation graph
        // and is provided using the Hilt-generated ViewModel factory
        viewModel = new ViewModelProvider(
            backStackEntry,
            HiltViewModelFactory.create(getContext(), backStackEntry)
        ).get(SharedViewModel.class);
    }
}

Compose

@Composable
fun MyAppNavHost() {
    // ...
    composable("myScreen") { backStackEntry ->
        val parentEntry = remember(backStackEntry) {
            navController.getBackStackEntry("parentNavigationRoute")
        }

        // ViewModel API available in hilt.hilt-navigation-compose
        // The ViewModel is scoped to the `parentNavigationRoute` Navigation graph
        // and is provided using the Hilt-generated ViewModel factory
        val parentViewModel: SharedViewModel = hiltViewModel(parentEntry)
        // ...
    }
}

Compartilhar dados entre fragmentos

É muito comum que dois ou mais fragmentos em uma atividade precisem se comunicar uns com os outros. Imagine um caso comum de fragmentos de visualização dividida (list-detail), em que você tem um fragmento em que o usuário seleciona um item de uma lista e outro que exibe o conteúdo do item selecionado. Esse caso nunca é trivial, porque ambos os fragmentos precisam definir alguma descrição de interface, e a atividade que a qual isso pertence precisa unir os dois. Além disso, ambos os fragmentos precisam lidar com o cenário em que o outro fragmento ainda tenha sido criado ou não esteja visível.

Esse ponto problemático comum pode ser resolvido usando objetos ViewModel. Esses fragmentos podem compartilhar um ViewModel usando o escopo de atividade deles para lidar com essa comunicação, conforme ilustrado pelo exemplo de código abaixo:

Visualizações

class SharedViewModel : ViewModel() {
    val selected = MutableLiveData()

    fun select(item: Item) {
        selected.value = item
    }
}

class ListFragment : Fragment() {

    private lateinit var itemSelector: Selector

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        itemSelector.setOnClickListener { item ->
            // Update the UI
        }
    }
}

class DetailFragment : Fragment() {

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: SharedViewModel by activityViewModels()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model.selected.observe(viewLifecycleOwner, Observer { item ->
            // Update the UI
        })
    }
}

Visualizações

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData getSelected() {
        return selected;
    }
}

public class ListFragment extends Fragment {
    private SharedViewModel model;

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        model.getSelected().observe(getViewLifecycleOwner(), item -> {
           // Update the UI.
        });
    }
}

Os dois fragmentos extraem a atividade que os contém. Dessa forma, quando os fragmentos recebem o ViewModelProvider, recebem a mesma instância de SharedViewModel, que tem escopo para essa atividade.

Essa abordagem oferece os seguintes benefícios:

  • A atividade não precisa fazer nada nem saber nada sobre essa comunicação.
  • Os fragmentos não precisam conhecer uns aos outros além do contrato SharedViewModel. Se um dos fragmentos desaparecer, o outro continuará funcionando normalmente.
  • Cada fragmento tem o próprio ciclo de vida e não é afetado pelo ciclo de vida do outro. Se um fragmento substituir o outro, a IU continuará funcionando sem problemas.

Substituir carregadores pelo ViewModel

As classes de carregadores, como o CursorLoader, geralmente são usadas para manter os dados na IU de um app em sincronia com um banco de dados. Você pode usar o ViewModel, com algumas outras classes, para substituir o carregador. Usar um ViewModel separa seu controlador de IU da operação de carregamento de dados, o que significa que você terá menos referências fortes entre as classes.

Em uma abordagem comum do uso de carregadores, um app pode usar um CursorLoader para observar o conteúdo de um banco de dados. Quando um valor no banco de dados é alterado, o carregador aciona automaticamente um recarregamento dos dados e atualiza a IU:

Figura 2. Carregar dados com carregadores.

O ViewModel trabalha em conjunto com a Room e o LiveData para substituir o carregador. O ViewModel garante que os dados sobrevivam a uma mudança na configuração do dispositivo. A Room informa seu LiveData quando o banco de dados muda, e o LiveData, por sua vez, atualiza a IU com os dados revisados.

Figura 3. Carregar dados com o ViewModel.

Usar corrotinas com o ViewModel

O ViewModel inclui compatibilidade com corrotinas do Kotlin. Para mais informações, consulte Usar corrotinas do Kotlin com Componentes da arquitetura do Android.

Mais informações

À medida que seus dados se tornam mais complexos, você pode optar por usar uma classe separada apenas para os carregar. O objetivo do ViewModel é encapsular os dados de um controlador de IU para permitir que eles sobrevivam às mudanças de configuração. Para saber mais sobre como carregar, manter e gerenciar dados entre as mudanças de configuração, consulte Como salvar estados de IU.

O Guia para arquitetura de apps Android sugere a criação de uma classe de repositório para lidar com essas funções.

Outros recursos

Para ver mais informações sobre a classe ViewModel, consulte os recursos a seguir.

Amostras

Codelabs

Blogs

Vídeos