I have a fragment, which contains LiveData, and SwiperRefreshLauout. In onCreateView I have set the observer for some String.
User, while playing with fragment, can change the value of this string, but it should be only temporary. Whenever user will trigger onRefreshListener, the String should get "default" data, that came from observer.
I'd like to know, if there's any way to call data from observer again, in onRefreshListener, or I have to remove observer from onCreateView, and make another one after onRefreshListener is called?
Dummy scenario:
We have String, called test. In OnCreateView, we are setting the observer to the ViewModel, and get some data from database to the test string. Let's say it'll be name - "Mark". When user will pick from spinner another name: "Carl" the test string will have value of "Carl" now. User would like to refresh the UI of layout, and now, the test string should have value - "Mark" again.
If I understand your situation correctly, here I have a suggestion:
In the onCreateView setup an observer of your live data, viewModel.data.observe(...)
At the same time you could call the function responsible for fetching the data, viewModel.fetchData("initialValue"). On the other hand, since this is the initial call, you could use the init section of the View Model class and call there the initial fetch. init { fetchData("initialValue") }
Then, everytime the pull to refresh callback is executed, inside the onRefresh you could call the function to fetch the data, but this time with the temp value viewModel.fetchData("defaultValue")
So, everytime you call the viewModel.fetchData("anyValue") the data LiveData will be refreshed and since you are already subscribed in the onCreateView then you will receive the updates there.
if this is not clear, please share some code in this way, I can give better advise.
Related
If I call lets say some function A in init block of View Model which will update live data and after that I m observing this live data in onViewCreated of activity. Then it is correct or not.? It will get that value which is stored in live data? Or I have to call function A after observing live data in activity.
And if it is working why it will work?
LiveData is just a data holder. It will store the latest value of the state. If you call function A in the init block, the live data will probably get its value before you start observing it in your Fragment/Activity i.e. when you observe it, you will get the new value of live data updated by function A.
If you call function A after setting up the observer, you will probably see the initial value of the LiveData first and then the new value will be observed.
In either case, it doesn't make much of a difference, because the value set by function A will anyway be processed. But is you want to set the value of LiveData as soon as the Activity/Fragment loads, init block of the ViewModel is a good choice.
Also note that if you call function A from the Fragment/Activity, it will be called every time the view gets destroyed and recreated. So if you are doing some operation in function A which should not be repeated on every view recreation, ViewModel's init block is the only option here.
I'm practice in using Navigation component. Now i have a trouble. I'm using Room database and i'm update it in my Fragment multiple times. Somehow it's anyway taking my last result no matter that i'm updating it so i decided that i need to update my fragment but i don't know how. I'm already found solutions like to detach and then attach fragment but i don't know how to do it because in my MainActivity i'm attaching to my fragment through "setupActionBarWithNavController".
I think you are overcomplicating matters. If you are using Room to manage your data, and the values in the database are getting updated by your code, then you can simply use LiveData. You don't need to reload your fragment at all.
Have your Room DAO query return a LiveData<MyType> instead of just a MyType object (for example). You can then "observe" the LiveData in your Fragment, and write a callback function to update your UI every time the value returned by the query changes. See this page for more info.
This basically means that when you tell your model to updateInsulin, once the database value is changed, the LiveData watching your insulin level will change and trigger your Observer. The Observer can then do something like change the text of a TextView or naviagte to a different Fragment.
I want to initialize the LiveData value when the app is launched, not every time the orientation changes.
Can I use the constructor of the subclass of ViewModel for it?
Instead of LiveData which pushes the last value to observers for every config change (like a RxJava BehaviourSubject), you should use something which pushes the event once.
You can use:
SingleLiveEvent: Send the event to only 1 observer, check here and here or alternatives here
LiveEvent: Send the event to all the observers, check here
Both of those approaches will not cache events, which means that an observer should be already observing the *LiveEvent to receive it
You can find articles online about alternative approaches but the philosophy behind them is probably the same
I'm trying to implement the recommended architecture by Google and in a tutorial they show this diagram:
So I have a MainActivity and when the app starts it should go and fetch some data from the internet. I do those network operations in the Repository. Now my problem is that I don't know how to communicate properly between activities and Repository. For example MainActivity starts and immediately display a circular progress bar while Repository fetches the data. How can I stop the animation in MainActivity as soon as the data is inserted to the database? I guess I could call observe() on the LiveData and wait for onChanged(). Is there a better approach? What if there is no new data? Then onChanged() wouldn't be called...
Maybe I could send intent from Repository to MainActivity when there is no data so MainActivity knows it should stop the animation and if it doesn't receive the intent it just waits for onChanged()?
I guess I just don't feel confortable with the onChanged() method because I will never be sure of the operation it corresponds to. Maybe before the data from the network arrived there was some other data inserted which trigged onChanged() which would then stop the loading animation before it was supposed to.
Regarding your issue in the comments, which I believe to answer your main question also.
You need to observe from your UI (Activity / Fragment) to a progress LiveData in your ViewModel. That could be working with a Boolean (LiveData<Boolean>). To represent the progress view being visible or not.
That in turn needs to take an identical LiveData from the Repository (declared in the Repository as a MutableLiveData). You then post updates to the progress MutableLiveData in the Repository.
Now, whenever the MutableLiveData receives a change, that exists in your ViewModel as it shares the variable reference, and it will pass to the observer in your UI.
-
Alternatively, you could return a LiveData<Boolean> from the method in your Repository that pulls data. That would then be observable in your UI.
Instead of Boolean, you could also use a more complicated structure containing more information. A message, error code, etc.
Now I read the Firebase documentation for android. I saw all the events onDataChange etc. But I didn't get 1 thing. Let's say I want to initialize my activity with some information from the database. No data has changed, no nothing. So it seems this event onDataChange won't help me. How can I explicitly say something like "go and fetch the data from firebase".
When you attach a ValueEventListener, its onDataChange method is called "immediately" with a snapshot of the current data.
If you attach the listener with addValueEventListener it will (in addition) be called upon any subsequent change to the data.
You could create a method that when the activity is initialized, so is a data structure with info from the database.
Retrieving data is pretty explicit here: https://firebase.google.com/docs/database/admin/retrieve-data
Also, you should be really careful because you should be using callbacks. If the activity needs to populate its views depending on that data, you should be aware of having that data before creating the views or trying to instantiate with null objects.
You can read the data only once, see:
https://firebase.google.com/docs/database/android/read-and-write#read_data_once
You "use the addListenerForSingleValueEvent() method to simplify this scenario: it triggers once and then does not trigger again."