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:
AndroidViewModelFactory
se a classeApplication
for necessária.AbstractSavedStateViewModelFactory
se for necessário transmitirSavedStateHandle
como uma dependência.
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.
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:

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.

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
- Amostra básica dos Componentes da arquitetura do Android (link em inglês)
- Sunflower (link em inglês), um app de jardinagem que ilustra as práticas recomendadas de desenvolvimento com o Android Jetpack.
Codelabs
- Android Room com View (Java) (Kotlin)
- Codelab de componentes compatíveis com ciclo de vida do Android
Blogs
- ViewModels: exemplo simples (link em inglês)
- ViewModels: Persistence, onSaveInstanceState(), Restoring UI State e Loaders (link em inglês)
- ViewModels e LiveData: Padrões + AntiPadrões (link em inglês)
- Kotlin sem mistério: noções básicas sobre a sintaxe do Shorthand Lambda (link em inglês)
- Kotlin sem mistério: funções de escopo (link em inglês)
- Kotlin sem mistério: quando usar os acessadores personalizados (link em inglês)
- Carregamento de dados de ciclos de vida com o Architecture Components (link em inglês)