onRetainCustomNonConfigurationInstance deprecated in AndroidX - android

onRetainCustomNonConfigurationInstance was deprecated in AndroidX when the first version got released back in 2018! As the AndroidX Activity library says in the release notes:
onRetainCustomNonConfigurationInstance has been deprecated. Use a ViewModel for storing objects that need to survive configuration changes.
I just want a single object to survive configuration changes, this was the purpose of onRetainCustomNonConfigurationInstance!
For my use case, I want an instance of a Dagger graph to survive configuration changes in an Activity but using AAC ViewModel doesn't feel right to me for that use case. Dagger injects my ViewModels, I don't want to wrap my graph in another ViewModel just for the sake of making it survive configuration changes.
Is there any other way I can make an object survive configuration changes?

Using ViewModel is the most accurate and recommended way to make an object survive configuration changes, you should use it. You could've used onSaveInstanceState but that'd force all the objects needing to support Parcelable, that's not only reasonable, it's sometimes impossible.
To replace onRetainCustomNonConfigurationInstance, you can use the same APIs that support ViewModel in a way that handles all the complexity for you.
You can use this implementation of LongLastingElement API (code here) that uses ViewModel under the hood and removes all the boilerplate code to make an object survive configuration changes.
For your example, to make the Dagger graph survive configuration changes with this API, the code would look like this:
class LoginActivity : AppCompatActivity() {
private lateinit var loginComponent: LoginComponent
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loginComponent = LongLastingElement.getInstance<LoginComponent>(this).getElement {
(applicationContext as MyDaggerApplication).appComponent.loginComponent().create()
}
loginComponent.inject(this)
}
}
LongLastingElement has a static method called getInstance where you specify the type of the object that needs to be stored and you pass a lifecycle owner in. Then, the getElement method is called, it receives a lambda as a parameter that needs to create an instance of the object you want to store.
Since the Lifecycle owner is used to obtain the LongLastingElement instance, the lambda that getElement takes as a parameter will be only called once. It'll create an instance of the object the very first time is called and the same instance will be reused after configuration changes and subsequent calls to getElement. This works for any lifecycle owner such as Activities and Fragments.

Related

Why should we do network call in ViewModel (Android)?

We use ViewModels for storing and manipulating data that is to be used by Views in Activity/Fragment - as data in ViewModel survives configuration change which helps us to retain UI state. It is one of the reasons we do network call operations in ViewModel, but is there any other reason we do network call operations in ViewModel?
I read somewhere that if we do network calls in ViewModels, the call does not happen again on configuration change? But I'm pretty sure API call is happening again in my Fragment on changing device orientation.
To overcome this problem you can call your function in init method of viewmodel makeApiCall() to prevent second call due to onViewCreated method. And you can store api result into livedata.
Alternatively, You can also use LiveData scope like this:
val makeApiCall: () -> LiveData<List<Data>> = {
liveData {
emit(repository.fetchData()) // API call
}
}
Call makeApiCall lambda function from your onViewCreated, now API call will emit only one time and your live data will get observed in onViewCreated.
This is the one of the main advantage of viewmodel to prevent API call on orientation change.
Another advantage is, Suppose if you close the screen and now API call is no longer needed so, if you are using RxJava you need to cancel/dispose API call to release resource so you can perform it in onCleared method of viewModel irrespective of orientation change.
Or if you are using coroutine you can use LiveData scope and viewModel
scope no need to care about canceling coroutine. it's managed by self.
We keep api hit in viewModel because of following reasons as per my practices
1)It reduces the coupling between Android components and non Android components
2)You can reuse the same ViewModel for some other screen as well
3) After fetching data you store that data in you liveData holder which can be used data to your UI on it's configuration change without making api hit

Can dagger make android activity creation slower?

I have property data manager in Activity A and I am instantiating it's value instance in activity A onCreate() through dagger component.
override fun onCreate(savedInstanceState: Bundle?){
datatManager = coreComponent().provideDataManager()
}
My questions(probably a silly ones) are: 1] Does dagger generate the code and instantiate the object when I call it on onCreate()? or Dagger being compile time, it already have all the classes ready behind the scene on which Data Manager has dependency? and just pas me the reference when I need it? 2] Does this make creating/starting an activity any slower?
Dagger generates code during compilation time, so the code itself is "ready", but that doesn' mean class instances are. Dagger creates instances every time you access them by default, unless you use a scoping mechanism such as #Singleton.
If an injected instance is heavy (i.e. does a lot in its constructor) then yes, it can negatively affect your activity creation time.

Is there a data loss while switching to darkmode

When the Dark mode is enabled using the AppCompatDelegate.MODE_NIGHT_YES in kotlin, the activity or the fragment is recreated. If we are calling an API in the same class it will be recalled. Is there a way to eliminate recalling the API.
The ideal way to handle this is by using ViewModel.
As the official documentation says:
The ViewModel class is designed to store and manage UI-related data in
a lifecycle conscious way. The ViewModel class allows data to survive
configuration changes such as screen rotations.
What it means, is that your ViewModel will survive your UI recreation and there will be no data loss or API double calls.
Create your ViewModel class.
class MyViewModel : ViewModel() {
fun callAPI() {
// call you're API here
}
}
Just obtain your ViewModel once in Fragment's onCreate() lifecycle callback.
val model = ViewModelProviders.of(this)[MyViewModel::class.java]
As you want that your API call will be done onle once per screen, make it in the ViewModel's construcor (for example).
init {
callAPI()
}
Please read more about this here.

Android Architecture Components: ViewModel keeps getting re-initialised

I have an activity that uses the ViewModel architecture component:
class RandomIdViewModel : ViewModel() {
var currentId : MutableLiveData<String?> = MutableLiveData()
init {
currentId.value = UUID.randomUUID().toString()
}
}
And then in my Activity I have this in the onCreate() method:
viewModel = ViewModelProviders.of(this).get(RandomIdViewModel::class.java)
viewModel.currentId.observe(this, idObserver)
Every time I rotate my phone the Id changes. So I am fairly confused as to why init is being called when I set the viewModel object.
EDIT
I have been looking at the saving state UI guidelines and it definitely appears that the ViewModel should maintain it's data throughout simple configuration changes:
ViewModel is ideal for storing and managing UI-related data while the user is actively using the application. It allows quick access to UI data and helps you avoid refetching data from network or disk across rotation, window resizing, and other commonly occurring configuration changes. ...
ViewModel is ideal for storing and managing UI-related data while the user is actively using the application. It allows quick access to UI data and helps you avoid refetching data from network or disk across rotation, window resizing, and other commonly occurring configuration changes
It appears that having a global variable in the activity that is stores a reference to the ViewModel as a once off causes the issue. All the examples seem to use the VM in a local variable, which doesn't work for me (I don't want my observers to be declared inline as it starts making the code quite messy1). The local variable seems to get a new instance every time a config change occurs. However if I create a method:
private fun viewModel() = ViewModelProviders.of(this).get(RandomIdViewModel::class.java)
and I call this whenever I need the VM. I think this is a bug that will most likely be resolved in the future.
1As a side note I also need to point out that I also had to remove my observers when the activity was not using them. This was another reason why I couldn't just inline the definition of the observers as they happen in different lifecycle events:
override fun onResume() {
super.onResume()
viewModel().currentId.observe(this, idObserver)
}
override fun onPause() {
viewModel().currentId.removeObserver(idObserver)
super.onPause()
}

Does ViewModel survive Activity save and restore?

Instances of the new ViewModel class can survive configuration changes if used in the following fashion:
mViewModel = ViewModelProviders.of(this).get(MyViewModel.class);
However, in addition to configuration changes, there is also a save-restore scenario when the entire application's process is being killed.
Will fields' values inside ViewModel be preserved during save-restore scenario?
Edit: based on the answer to this question, I wrote this article: Android ViewModel Architecture Component is Dangerous
According to ViewModelProvider documentation (check get method), ViewModel is not preserved when app's process is killed:
The created ViewModel is associated with the given scope and will be retained as long as the scope is alive (e.g. if it is an activity, until it is finished or process is killed)
In addition check Ian Lake answer to similar question:
https://medium.com/#ianhlake/you-are-correct-the-viewmodel-is-destroyed-if-your-process-is-killed-by-android-ef611fcd51e6
You are correct: the ViewModel is destroyed if your process is killed by Android. Just like before, you should use onSaveInstanceState() to store any data you must have to later recreate your Activity in the same state as before.
I also recommend reading
Android ViewModel Architecture Component is Dangerous.
It seems Google offers a solution for this now
Saved State module for ViewModel
UI State is usually stored or referenced in ViewModel objects, not
activities; so using onSaveInstanceState() requires some boilerplate
that this module can handle for you.
When the module is set up, ViewModel objects receive a
SavedStateHandle object via its constructor. This is a key-value map
that will let you write and retrieve objects to and from the saved
state. These values will persist after the process is killed by the
system and remain available via the same object.
Setup
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:1.0.0-rc02' (November 7, 2019)
Usage
In order to set up a ViewModel to receive a SavedStateHandle you need
to create them using a Factory that extends
AbstractSavedStateVMFactory.
SavedStateViewModel vm = new ViewModelProvider(this, new SavedStateVMFactory(this))
.get(SavedStateViewModel.class);
After that your ViewModel can have a constructor that receives a
SavedStateHandle:
public class SavedStateViewModel extends ViewModel {
private SavedStateHandle mState;
public SavedStateViewModel(SavedStateHandle savedStateHandle) {
mState = savedStateHandle;
}
...
}
Storing and retrieving values
The SavedStateHandle class has the methods you expect for a key-value map:
get(String key)
contains(String key)
remove(String key)
set(String key, T value)
keys()

Categories

Resources