Em algum momento, notei conversas periódicas sobre como o
ViewModel realmente funciona a partir dos componentes arquiteturais do Google. Percebendo que eu mesmo não entendi, entrei na Internet e fiquei surpreso ao descobrir que há uma quantidade incrível de artigos idênticos sobre como cozinhar o ViewModel, fazer amizade com o LiveData, passar dependências pelo Dagger, copular com o RxJava e outros títulos de vários graus de utilidade, no entanto, não há quase nada sobre o que está acontecendo lá dentro.
Então, eu vou tentar preencher a lacuna.
Cuidado
TL; DR, se você sentir pena do tempo - role para a saída, perderá pouco.
Portanto, a primeira coisa que você deve prestar atenção é que existem 2 pacotes diferentes de componentes de arquitetura com o ViewModel, a saber:
1) Antigo
android.arch.lifecycle2) Novo
androidx.lifecycleSpoiler : Não há muita diferença entre eles.
Todo o trabalho está por trás do desafio:
ViewModelProviders.of(activity).get(MyViewModel::class.java)
Vamos começar com o método
public static ViewModelProvider of(@NonNull FragmentActivity activity) { return of(activity, null); } public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { Application application = checkApplication(activity); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(ViewModelStores.of(activity), factory); }
checkApplication apenas verifica nulo, e
AndroidViewModelFactory é apenas um singleton seguro para threads que contém Application. Portanto, eles não são de interesse especial, o mais interessante está no método
ViewModelStores.of :
public static ViewModelStore of(@NonNull FragmentActivity activity) { if (activity instanceof ViewModelStoreOwner) { return ((ViewModelStoreOwner) activity).getViewModelStore(); } return holderFragmentFor(activity).getViewModelStore(); }
À primeira vista, parece bastante estranho - por que um
FragmentActivity deve verificar a presença da interface
ViewModelStoreOwner se
já a implementa ? - Esse nem sempre foi o caso - até o distante fevereiro de 2018, quando a
biblioteca de suporte versão
27.1.0 foi lançada , o FragmentActivity nunca havia implementado o ViewModelStoreOwner. Ao mesmo tempo, o ViewModel trabalhou por si mesmo.
Então, vamos começar com o caso antigo - o método
holderFragmentFor foi
lançado :
public static HolderFragment holderFragmentFor(FragmentActivity activity) { return sHolderFragmentManager.holderFragmentFor(activity); }
Em seguida, obtenha ou crie um novo fragmento de
titular :
HolderFragment holderFragmentFor(FragmentActivity activity) { FragmentManager fm = activity.getSupportFragmentManager(); HolderFragment holder = findHolderFragment(fm); if (holder != null) { return holder; } holder = mNotCommittedActivityHolders.get(activity); if (holder != null) { return holder; } if (!mActivityCallbacksIsAdded) { mActivityCallbacksIsAdded = true; activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks); } holder = createHolderFragment(fm); mNotCommittedActivityHolders.put(activity, holder); return holder; }
Bem, o
HolderFragment em si
, é claro,
retido public HolderFragment() { setRetainInstance(true); }
Na verdade, o objeto
ViewModelStore é armazenado nele, que, por sua vez, contém um pacote de
ViewModel :
public class ViewModelStore { private final HashMap<String, ViewModel> mMap = new HashMap<>(); final void put(String key, ViewModel viewModel) { ViewModel oldViewModel = mMap.put(key, viewModel); if (oldViewModel != null) { oldViewModel.onCleared(); } } final ViewModel get(String key) { return mMap.get(key); } public final void clear() { for (ViewModel vm : mMap.values()) { vm.onCleared(); } mMap.clear(); } }
Vamos voltar ao caso em que a versão da biblioteca de suporte é 27.1.0 e superior. FragmentActivity já implementa a interface ViewModelStoreOwner, ou seja, implementa o único método
getViewModelStore :
public ViewModelStore getViewModelStore() { if (this.getApplication() == null) { throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call."); } else { if (this.mViewModelStore == null) { FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance(); if (nc != null) { this.mViewModelStore = nc.viewModelStore; } if (this.mViewModelStore == null) { this.mViewModelStore = new ViewModelStore(); } } return this.mViewModelStore; } }
Aqui vou simplificar um pouco -
NonConfigurationInstances é um objeto que não deve depender da configuração (obviamente do nome), que fica na
Activity e varre dentro do
ActivityClientRecord por
ActivityThread durante a recreação entre
onStop e
onDestroyEm geral, parece bem engraçado - em vez de um golpe na vida com uma transferência do ViewModel dentro do fragmento de
retenção , os desenvolvedores fizeram uma jogada complicada - eles usaram exatamente o mesmo mecanismo, mas se livraram da necessidade de criar um fragmento extra a cada vez.
A atividade sempre teve um método
onRetainNonConfigurationInstance interessante. Na classe Activity, ele basicamente não fez nada. Em geral:
public Object onRetainNonConfigurationInstance() { return null; }
A descrição na documentação é promissora:
Chamado pelo sistema, como parte da destruição de uma atividade devido a uma alteração na configuração, quando se sabe que uma nova instância será criada imediatamente para a nova configuração. Você pode retornar qualquer objeto que desejar aqui, incluindo a própria instância de atividade, que pode ser recuperada posteriormente chamando getLastNonConfigurationInstance () na nova instância de atividade.

Ou seja, o que não colocar lá - ele será rastreado em
getLastNonConfigurationInstance () depois de recriar a Atividade. Os desenvolvedores de componentes arquitetônicos se aproveitaram disso. Dos pontos negativos - até 4 andróides não funcionam, terá que ser feito da maneira antiga através de reter fragmento.
O método
clear () do ViewModel foi chamado de maneira muito simples - no método
onDestroy FragmentActivity.
protected void onDestroy() { super.onDestroy(); if (this.mViewModelStore != null && !this.isChangingConfigurations()) { this.mViewModelStore.clear(); } this.mFragments.dispatchDestroy(); }
De fato, com o Androidx, quase tudo é igual, a única diferença é que o método getViewModelStore () não está mais em FragmentActivity, mas em
ComponentActivity , do qual FragmentActivity é herdado no AndroidX. Somente a chamada para o método clear () foi alterada; foi removida de onDestroy para um retorno de chamada independente, criado no construtor ComponentActivity:
getLifecycle().addObserver(new GenericLifecycleObserver() { @Override public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } });
Para o protocolo - durante a criação do artigo, foram utilizados os seguintes:
Biblioteca de suporte 27.0.0, 28.0.0
androidx.lifecycle: lifecycle-viewmodel: 2.0.0
androidx.lifecycle: lifecycle-extensions: 2.0.0
android.arch.lifecycle: extensions: 1.1.1
android.arch.lifecycle: viewmodel: 1.1.1
Conclusões:
- O ViewModel realmente sobreviveu à recriação de atividade no fragmento de
retenção até a biblioteca de suporte 27.1.0 que apareceu em fevereiro de 2018
- A partir da
biblioteca de suporte versão
27.1.0 em diante, bem como no
AndroidX ViewModel, esperei para recriar a atividade em
FragmentActivity.NonConfigurationInstances (
ComponentActivity.NonConfigurationInstances for AndroidX), na verdade o mesmo mecanismo pelo qual retém os fragmentos funciona, mas a criação de um fragmento extra não é necessária , todos os ViewModel são enviados "ao lado de" com fragmentos de retenção.
- O mecanismo do ViewModel é quase o
mesmo na biblioteca AndroidX e Support
- Se você precisar repentinamente (sim, não consigo imaginar o porquê) arrastar dados que devem permanecer durante a atividade, mas
levar em consideração a recreação - use os
métodos onRetainNonConfigurationInstance () /
getLastNonConfigurationInstance ()- Qual é a decisão antiga, qual a aparência do novo algo entre um hack e muletas documentadas