How to use Dagger 2 to Inject ViewModel of same Fragments inside ViewPager
I want to recap the original question, here's it:
I am currently using the working
fine_branch
, but I want to know, why would using scope break this.
As per my understanding your have an impression, that just because you are trying to obtain an instance of ViewModel
using different keys, then you should be provided different instances of ViewModel
:
// in first fragment
ViewModelProvider(...).get("true", PagerItemViewModel::class.java)
// in second fragment
ViewModelProvider(...).get("false", PagerItemViewModel::class.java)
The reality, is a bit different. If you put following log in fragment you'll see that those two fragments are using the exact same instance of PagerItemViewModel
:
Log.i("vvv", "${if (oneOrTwo) "one:" else "two:"} viewModel hash is ${viewModel.hashCode()}")
Let's dive in and understand why this happens.
Internally ViewModelProvider#get()
will try to obtain an instance of PagerItemViewModel
from a ViewModelStore
which is basically a map of String
to ViewModel
.
When FirstFragment
asks for an instance of PagerItemViewModel
the map
is empty, hence mFactory.create(modelClass)
is executed, which ends up in ViewModelProviderFactory
. creator.get()
ends up calling DoubleCheck
with following code:
public T get() {
Object result = instance;
if (result == UNINITIALIZED) { // 1
synchronized (this) {
result = instance;
if (result == UNINITIALIZED) {
result = provider.get();
instance = reentrantCheck(instance, result); // 2
/* Null out the reference to the provider. We are never going to need it again, so we
* can make it eligible for GC. */
provider = null;
}
}
}
return (T) result;
}
The instance
is now null
, hence a new instance of PagerItemViewModel
is created and is saved in instance
(see // 2).
Now the exact same procedure happens for SecondFragment
:
- fragment asks for an instance of
PagerItemViewModel
map
now is not empty, but does not contain an instance ofPagerItemViewModel
with keyfalse
- a new instance of
PagerItemViewModel
is initiated to be created viamFactory.create(modelClass)
- Inside
ViewModelProviderFactory
execution reachescreator.get()
whose implementation isDoubleCheck
Now, the key moment. This DoubleCheck
is the same instance of DoubleCheck
that was used for creating ViewModel
instance when FirstFragment
asked for it. Why is it the same instance? Because you've applied a scope to the provider method.
The if (result == UNINITIALIZED)
(// 1) is evaluating to false and the exact same instance of ViewModel
is being returned to the caller - SecondFragment
.
Now, both fragments are using the same instance of ViewModel
hence it is perfectly fine that they are displaying the same data.