Reputation: 28189
This is how the guides recommend retrieving the ViewModel of an activity for the first time, which normally happens inside the onCreate
:
public class FooActivity extends AppCompatActivity {
@Inject
ViewModelProvider.Factory viewModelFactory;
@Override
protected void onCreate(Bundle savedInstanceState) {
//...
FooViewModel viewModel = new ViewModelProvider(this, viewModelFactory)
.get(FooViewModel.class);
}
}
Now that has to be a costly operation, since we are creating a new instance of ViewModelProvider. It is also somewhat verbose. What if I need the ViewModel again outside the onCreate
?
I initially though of caching it in a private variable inside the activity, but can't that leak the ViewModel? E.g.:
public class FooActivity extends AppCompatActivity {
FooViewModel viewModel;
@Inject
ViewModelProvider.Factory viewModelFactory;
@Override
protected void onCreate(Bundle savedInstanceState) {
//...
viewModel = new ViewModelProvider(this, viewModelFactory)
.get(FooViewModel.class);
}
@Override
protected void onStop() {
//...
viewModel.someMethod();
}
}
Would this approach be safer if I manually set the viewModel
variable to null in the onDestroy
?
Another alternative approach would be to somehow retrieve the associated ViewModel from the activity. AppCompatActivity has a ViewModelStore, but calling getViewModelStore()
returns an object where only a clear
method is available.
So what is the recommended way of getting the associated ViewModel for a second time in a fragmentless activity?
Upvotes: 1
Views: 72
Reputation: 4497
Even in the official Android documentation it initialized the ViewModel as a class field like this:
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
}
So you even do not need to initialize it in onCreate
function.
Also based on the Android Docs ViewModels scoped to the closest ViewModelStoreOwner
.
So in these scenarios:
import androidx.lifecycle.ViewModelProvider;
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);
}
There would be NO Memory Leaks because ViewModel knows its scope and will be destroyed out of its scope.
Feel free to create it once as a field without any problem and use it outside the onCreate
function.
Upvotes: 0
Reputation: 1007544
I initially though of caching it in a private variable inside the activity, but can't that leak the ViewModel?
Only if the activity itself is leaked. The lifetime of an activity's viewmodel is longer than the lifetime of the activity itself, since the viewmodel exists in part to survive configuration changes, which involves multiple instances of the activity sharing a viewmodel instance.
Would this approach be safer if I manually set the
viewModel
variable to null in theonDestroy
?
Only if you leak your activities, which is a much bigger problem.
So what is the recommended way of getting the associated ViewModel for a second time in a fragmentless activity?
In Java, hold onto it in a field in the activity, as you have in your second code snippet.
Upvotes: 1