My app is a basic news app which fetches data from JSON provided by Guardian API.
I parsed the values from JSON using raw java code (not using retrofit).
Then I get the LiveData in NewsFeedViewModel class which extends as AndroidViewModel.
And then in the fragment, I submit list to adapter.
These are the issues I'm facing:
1) at first, if the articles to show is set to 10, then if i go to settings and change it to 2, then the last 8 articles are disappearing but the white space /gap is not going. I can still scroll through the empty gap.
2) if i change the number of articles value constantly, then app is becoming un-scrollable.
And i have a few more doubts, how to refresh the data manually when swipeToRefresh is happened?
This is my project github link: https://github.com/sdzshn3/News24-7-RV
Video sample of the issue happening in app: https://drive.google.com/file/d/1gr_fabS2rqREuyecvGSG3IQ_jXOowlW7/view?usp=drivesdk
In kotlin style:
class RefreshableLiveData<T>(
private val source: () -> LiveData<T>
) : MediatorLiveData<T>() {
private var liveData = source()
init {
this.addSource(liveData, ::observer)
}
private fun observer(data: T) {
value = data
}
fun refresh() {
this.removeSource(liveData)
liveData = source()
this.addSource(liveData, ::observer)
}
}
Example:
class MainActivity : AppCompatActivity() {
private val viewModel: MyViewModel by viewModel()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.goals.observe(this) { result ->
// update UI
}
// refresh
viewModel.refresh()
}
}
class MyViewModel(useCase: MyUseCase): ViewModel() {
private val _goals = RefreshableLiveData {
useCase.getGoals()
}
val goals: LiveData<Result<List<GoalItem>>>
get() = _goals.map(GoalItem::fromEntity)
fun refresh() {
_goals.refresh()
}
}
class MyUseCase {...}
...
You need to do exactly what I did in this Reddit post:
public class RefreshLiveData<T> extends MutableLiveData<T> {
public interface RefreshAction<T> {
private interface Callback<T> {
void onDataLoaded(T t);
}
void loadData(Callback<T> callback);
}
private final RefreshAction<T> refreshAction;
private final Callback<T> callback = new RefreshAction.Callback<T>() {
#Override
public void onDataLoaded(T t) {
postValue(t);
}
};
public RefreshLiveData(RefreshAction<T> refreshAction) {
this.refreshAction = refreshAction;
}
public final void refresh() {
refreshAction.loadData(callback);
}
}
Then you can do
public class YourViewModel extends ViewModel {
private final GithubRepository githubRepository;
public YourViewModel(GithubRepository githubRepository, SavedStateHandle savedStateHandle) {
this.githubRepository = githubRepository;
}
private final LiveData<String> userId = savedStateHandle.getLiveData("userId"); // from args
private final RefreshLiveData<List<Project>> refreshLiveData = Transformations.switchMap(userId, (uId) -> {
return githubRepository.getProjectList(uId);
});
public void refreshData() {
refreshLiveData.refresh();
}
public LiveData<List<Project>> getProjects() {
return refreshLiveData;
}
}
And then repository can do:
public RefreshLiveData<List<Project>> getProjectList(String userId) {
final RefreshLiveData<List<Project>> liveData = new RefreshLiveData<>((callback) -> {
githubService.getProjectList(userId).enqueue(new Callback<List<Project>>() {
#Override
public void onResponse(Call<List<Project>> call, Response<List<Project>> response) {
callback.onDataLoaded(response.body());
}
#Override
public void onFailure(Call<List<Project>> call, Throwable t) {
}
});
});
return liveData;
}
Related
Since I'm new to programming Android apps I followed a tutorial on how to use the Android Architecture Components and Firebase for implementing the MVVM (using LiveData, ViewModel, etc.).
The tutorial I followed can be found here:
Part 1
Part 2
Part 3
I'm now left with what I think is a decent implementation of the MVVM, but I can not wrap my head around how I am supposed to pass query parameters to it. Right now I need to hardcode the ID of the document I want to retrieve:
public class AlarmDAO {
private FirebaseFirestore firebaseFirestore = FirebaseFirestore.getInstance();
public AlarmLiveData getFirestoreLiveData() {
DocumentReference documentReference = firebaseFirestore.collection(Collection.ALARMS.name).document("5RxJNuNyhDJlz49wpBkw");
return new AlarmLiveData(documentReference);
}
}
That then gets called by a class extending ViewModel.
public class AlarmViewModel extends ViewModel {
private AlarmDAO DAO = new AlarmDAO();
private AlarmLiveData liveData = null;
public LiveData<Alarm> getAlarmLiveData() {
liveData = DAO.getFirestoreLiveData();
return liveData;
}
public LiveData<Alarm> getAlarm() {
return liveData.alarm;
}
}
And then I observe that data in my activity:
model.getAlarmLiveData().observe(this, Observable -> {});
model.getAlarm().observe(this, alarm -> {
if (alarm != null) {
alarmTextView.setText(alarm.getTest());
else {
Log.d(TAG, "Waiting for data");
}
});
My problem is that I do not see a way of querying for a specific alarm. For instance model.getAlarm("someId"). I am under the impression that it should be done in the DAO and/or the ViewModel, but I can't figure out how. Another thing I do not understand is why I need to observe both model.getAlarmLiveData() and model.getAlarm() in my activity, as using only one does not work. The answer to both of those questions is most likely very simple, but thus far I haven't been able to figure it out.
For completeness: the Alarm class is nothing besides a getter and setter for two strings, and the AlarmLiveData class is below.
public class AlarmLiveData extends LiveData<Alarm> implements EventListener<DocumentSnapshot> {
private static final String TAG = AlarmLiveData.class.getSimpleName();
private Alarm alarmTemp = new Alarm();
private DocumentReference documentReference;
private ListenerRegistration listenerRegistration = () -> {};
public MutableLiveData<Alarm> alarm = new MutableLiveData<>();
public AlarmLiveData(DocumentReference documentReference) {
this.documentReference = documentReference;
}
#Override
protected void onActive() {
listenerRegistration = documentReference.addSnapshotListener(this);
super.onActive();
}
#Override
protected void onInactive() {
listenerRegistration.remove();
super.onInactive();
}
#Override
public void onEvent(#Nullable DocumentSnapshot documentSnapshot, #Nullable FirebaseFirestoreException e) {
if (documentSnapshot != null && documentSnapshot.exists()) {
alarmTemp = new Alarm();
alarmTemp.setId(documentSnapshot.getId());
alarmTemp.setTest(documentSnapshot.get("test").toString());
alarm.setValue(alarmTemp);
} else {
Log.d(TAG, "ERROR");
}
}
}
Thank you for reading, I'm looking forward to the answer(s)!
The reason you have to use both model.getAlarmLiveData() and model.getAlarm() looks to be that your AlarmLiveData class extends LiveData but sets a value for the contained MutableLiveData member variable instead of setting its own class value.
Inside your AlarmLiveData class:
// Comment out/Remove your 'public MutableLiveData<alarm> alarm' member variable from the top.
// You're going to want to set the value of the AlarmLiveData class itself instead.
// ...
// Then inside of your onEvent callback
#Override
public void onEvent(#Nullable DocumentSnapshot documentSnapshot, #Nullable FirebaseFirestoreException e) {
if (documentSnapshot != null && documentSnapshot.exists()) {
alarmTemp = new Alarm();
alarmTemp.setId(documentSnapshot.getId());
alarmTemp.setTest(documentSnapshot.get("test").toString());
// Set the value for the AlarmLiveData class directly
setValue(alarmTemp);
} else {
Log.d(TAG, "ERROR");
}
}
I'm not sure why you're creating a DAO class and I would most likely move that code directly into the AlarmViewModel class.
But, here is how you can alter your current DAO class if you don't want to remove it:
// Pass in the document id you want to create a document reference for
public AlarmLiveData getFirestoreLiveData(String documentId) {
DocumentReference documentReference = firebaseFirestore.collection(Collection.ALARMS.name).document(documentId);
return new AlarmLiveData(documentReference);
}
Your AlarmViewModel class would look something like this:
public class AlarmViewModel extends ViewModel {
private AlarmDAO DAO = new AlarmDAO();
private AlarmLiveData liveData = null;
// Make sure to take in the document id so you can create the corresponding LiveData
public LiveData<Alarm> getAlarmLiveData(String documentId) {
// Only create a new LiveData instance if the current one is null.
// This is helpful if you intend to use this as a Shared ViewModel.
if(liveData == null){
liveData = DAO.getFirestoreLiveData(documentId);
}
return liveData;
}
}
Finally, in your Activity:
// Pass in the document id and observe the ViewModel
model.getAlarmLiveData("MY_DOCUMENT_ID").observe(this, alarm -> {
if (alarm != null) {
alarmTextView.setText(alarm.getTest());
}else{
Log.d(TAG, "Waiting for data");
}
});
I'm recently learning about the architectural components and was following the old tutorial where they used the old method:
mainActivityViewModel =
new ViewModelProvider(this).get(MainActivityViewModel.class);
But in the documentation for the ViewModelProvider, the only constructors available are
ViewModelProvider(ViewModelStoreOwner, Factory) &
ViewModelProvider(ViewModelStore, Factory).
So I did something like this but I'm not sure what to do in the overridden method and it currently returns null that crashes the program.
public class MainActivity extends AppCompatActivity {
private NoteViewModel noteViewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
noteViewModel = new ViewModelProvider(this, new ViewModelProvider.Factory() {
#NonNull
#Override
public <T extends ViewModel> T create(#NonNull Class<T> modelClass) {
return null;
}
}).get(NoteViewModel.class);
noteViewModel.getAllNotes().observe(this, new Observer<List<NoteEntity>>() {
#Override
public void onChanged(List<NoteEntity> noteEntities) {
Toast.makeText(MainActivity.this,"Changed",Toast.LENGTH_LONG).show();
}
});
}
}
Is my approach correct? I'm absolutely lost right now. What am I supposed to return from the overridden method?
Use this
noteViewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(NoteViewModel.class;
We use custom factory, when we pass param to the constructor of ViewModel (apart from Application param).
and gradle dependency in case,
def lifecycle_version = "2.2.0"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
//
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
// ViewModel
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
First you need to initialize viewmodel in activity class like :
noteViewModel = ViewModelProvider(this).get(NoteViewModel.class);
In NoteViewModel.java
You need to define livedata variable for storing updated data provided by model and update post to view model
NoteViewModel.java file look like :
public class NoteViewModel extends AndroidViewModel {
AppRepository appRepository;
MediatorLiveData<List<NoteEntity>> mediatorData;
public NoteViewModel (#NonNull Application application) {
super(application);
mediatorData=new MediatorLiveData<>();
appRepository=new AppRepository((MyApplication).apiService, application.retrofit);
}
public MediatorLiveData<List<NoteEntity>> getMediatorLiveData() {
return mediatorVideoData;
}
public void getNoteEntry()
{
try {
mediatorData.addSource(appRepository.getNoteEntry(), new Observer<List<NoteEntity>>() {
#Override
public void onChanged(#Nullable List<NoteEntity> data) {
mediatorData.postValue(data);
}
});
}catch (Exception e)
{
}
}
}
In onCreate() of Mainactivity register observer like and call the API from view model like
noteViewModel.getMediatorLiveData().observe(this, new Observer<List<NoteEntity>>() {
#Override
public void onChanged(List<NoteEntity> noteEntities) {
Toast.makeText(MainActivity.this,"Changed",Toast.LENGTH_LONG).show();
}
});
noteViewModel.getNoteEntry();
AppRepository.java file look like
class AppRepository() {
ApiService apiService;
Retrofit retrofit;
public AppRepository(ApiService apiService ,Retrofit retrofit){
this.apiService = apiService;
this.retrofit = retrofit;
}
public MediatorLiveData<List<NoteEntity>> getNotes() {
MediatorLiveData<List<NoteEntity>> data = new MediatorLiveData<List<NoteEntity>>()
apiService.getNotes()
.enqueue(new Callback<List<NoteEntity>> {
#Override
void onResponse(
Call<List<NoteEntity>> call,
Response<List<NoteEntity>> response
) {
if (response.isSuccessful()) {
if(response.body()!=null){
data.postValue(response.body()); //successfull data
}else{
data.postValue(null); //error
}
} else {
data.postValue(null); //error
}
}
#Override
fun onFailure(
Call<List<NoteEntity>> call,
Throwable t
) {
data.postValue(null); //error
}
})
return data;
}
}
Kotlin Code.
In build.gradle add
implementation "androidx.activity:activity-ktx:1.1.0"
then
val noteViewModel by viewModels<NoteViewModel>()
Figured it out after 4 hours.
noteViewModel = new ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(this.getApplication())).get(NoteViewModel.class);
Also these dependencies should be added to the gradle file.
def room_version = "2.2.5"
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
Not sure if the error was caused because I missed the annotation dependency. But everything works now.
I'm just trying to learn mvvm, and I faced some issue - My List which should contain response from API are empty. I'm not sure why it is. Here's some code:
MainActivity
mViewModel = new ViewModelProvider(this).get(ViewModel.class);
mViewModel.getData().observe(this, new Observer<List<Model>() {
#Override
public void onChanged(List<Model> list) {
if (data.size() > 0) {
data.clear();
}
if (list != null) {
data.addAll(list);
Log.i(TAG, "onChanged: " + data.size());
}
}
});
ViewModel
private Repository mRepository;
private MutableLiveData<List<Model> liveData;
public ViewModel(#NonNull Application application) {
super(application);
mRepository = Repository.getInstance();
liveData = mRepository.getData();
}
public MutableLiveData<List<Model> getData(){
return liveData;
}
Repository
public MutableLiveData<List<Model>> getData(){
MutableLiveData<List<Model> mLiveData = new MutableLiveData<>();
mApiCall.callApi()
.enqueue(new Callback<List<Model>() {
#Override
public void onResponse(Call<List<Model> call, Response<List<Model> response) {
mLiveData.setValue(response.body());
}
#Override
public void onFailure(Call<List<Model> call, Throwable t) {
t.getMessage();
}
});
return mLiveData;
}
in function getData() you are returning a live data and in this line in your view model :
liveData = mRepository.getData();
you are assigning it to liveDate which is a mutable live data created in your view model and the problem is here.
when this assignment is happened the observer in the liveData variable in view model will be removed and that's why we should use switchMap like this :
private var result : LiveData<List<Response>> = MutableLiveData()
result : LiveData<Response> = Transformations.map(mRepository.getData()){
it
}
and now all you need to do is to observe on repo in your view like this :
viewmodel.result.observe(this, Observer{ list ->
// to do with the result
})
response.body() will provide with class APIResponse. But what you need as response is List.
To get the expected response as List, try response.body().getMetadata().getResults()
It could be either repository initialisation as well. Do try creating
public void init() {
mRepository = Repository.getInstance();
liveData = mRepository.getData();
}
use viewModel.init();
Model class properties should match with response JSON fields to get the model objects from the response.
Debug on the response.body() and see the data coming. if the model is empty then there would be a mismatch on the fields.
I´ve been looking for a suitable solution or best practice when I want to use Kotlin Flows with ordinary callbacks. My use case is that I write a kotlin library that uses Kotlin Flow internally and i have to assume that the users will use Java for instance. So I thought that the best solution is to overload a basic callback interface to my flow method and call it in collect something like this:
class KotlinClass {
interface Callback {
fun onResult(result: Int)
}
private fun foo() = flow {
for (i in 1..3) {
emit(i)
}
}
fun bar(callback: Callback) {
runBlocking {
foo().collect { callback.onResult(it) }
}
}
private fun main() {
bar(object : Callback {
override fun onResult(result: Int) {
TODO("Not yet implemented")
}
})
}
and in my Java Application i can simply use it like that:
public class JavaClass {
public void main() {
KotlinClass libraryClass = new KotlinClass();
libraryClass.bar(new KotlinClass.Callback() {
#Override
public void onResult(int result) {
// TODO("Not yet implemented")
}
});
}
}
I am not sure whats the way to go because I would like to have my Kotlin library that uses Flows usable in a good fashion for Java and Kotlin.
I came across callbackFlow but that seems to be only if I want to let´s call it flow-ify a callback-based API? Because I am quite new to Kotlin and Flows please apologise if my question is flawed in cause of missing some basic concepts of kotlin.
I would give the Java client more control over the flow. I would add a onStart and onCompletion method to your callback interface. Beside this I would use an own CoroutineScope - maybe customizable from the Java client. And I would not block the calling thread from within the Kotlin function - no runBlocking.
#InternalCoroutinesApi
class KotlinClass {
val coroutineScope = CoroutineScope(Dispatchers.Default)
interface FlowCallback {
#JvmDefault
fun onStart() = Unit
#JvmDefault
fun onCompletion(thr: Throwable?) = Unit
fun onResult(result: Int)
}
private fun foo() = flow {
for (i in 1..3) {
emit(i)
}
}
fun bar(flowCallback: FlowCallback) {
coroutineScope.launch {
foo().onStart { flowCallback.onStart() }
.onCompletion { flowCallback.onCompletion(it) }
.collect { flowCallback.onResult(it) }
}
}
fun close() {
coroutineScope.cancel()
}
}
Now the Java client is in full control how to start, collect and cancel the flow. For example you could use a latch to wait for completion, set an timeout and cancel the couroutine scope. This looks in the first place like a lot of code, but typically you will need this kind of flexibility.
public class JavaClass {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
KotlinClass libraryClass = new KotlinClass();
libraryClass.bar(new KotlinClass.FlowCallback() {
#Override
public void onCompletion(#Nullable Throwable thr) {
latch.countDown();
}
#Override
public void onResult(int result) {
System.out.println(result);
}
});
try {
latch.await(5, TimeUnit.SECONDS);
} finally {
libraryClass.close();
}
}
}
You don't need to create a interface in the Kotlin code. You can define bar like that:
fun bar(callback: (Int) -> Unit) {
runBlocking {
foo().collect { callback(it) }
}
}
From the Java code you can call the function like that:
public class JavaClass {
public static void main(String[] args) {
KotlinClass libraryClass = new KotlinClass();
libraryClass.bar(v -> { System.out.println(v); return Unit.INSTANCE; });
}
}
In case anyone wondering for a general solution. Here's our version of enhancement from #rene answer here.
Accept a generic type
A configurable coroutineScope
// JavaFlow.kt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
#InternalCoroutinesApi
class JavaFlow<T>(
private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) {
interface OperatorCallback <T> {
#JvmDefault
fun onStart() = Unit
#JvmDefault
fun onCompletion(thr: Throwable?) = Unit
fun onResult(result: T)
}
fun collect(
flow: Flow<T>,
operatorCallback: OperatorCallback<T>,
) {
coroutineScope.launch {
flow
.onStart { operatorCallback.onStart() }
.onCompletion { operatorCallback.onCompletion(it) }
.collect { operatorCallback.onResult(it) }
}
}
fun close() {
coroutineScope.cancel()
}
}
Java caller-side:
// code omitted...
new JavaFlow<File>().collect(
// compressImageAsFlow is our actual kotlin flow extension
FileUtils.compressImageAsFlow(file, activity),
new JavaFlow.OperatorCallback<File>() {
#Override
public void onResult(File result) {
// do something with the result here
SafeSingleton.setFile(result);
}
}
);
// or using lambda with method references
// new JavaFlow<File>().collect(
// FileUtils.compressImageAsFlow(file, activity),
// SafeSingleton::setFile
// );
// Change coroutineScope to Main
// new JavaFlow<File>(CoroutineScopeKt.MainScope()).collect(
// FileUtils.compressImageAsFlow(file, activity),
// SafeSingleton::setFile
// );
OperatorCallback.onStart and OperatorCallback.onCompletion is optional, override it as needed.
I am developing android application and I want to refresh viewModel livedata from second activity. When I get back to first activity data is not refreshed.
FirstActivity:
mViewModel = ViewModelProviders.of(this).get(MenuViewModel.class);
mViewModel.getMenus().observe(this, menuResponse -> {
if (menuResponse != null) {
resMenus.addAll(menuResponse.getMenus());
progressBar.setVisibility(View.GONE);
mAdapter.notifyDataSetChanged();
}
});
MenuViewModel:
public class MenuViewModel extends AndroidViewModel {
private MutableLiveData<MenuResponse> restMenuData;
private MenusRepository mRepository;
public MainActivityViewModel(#NonNull Application application) {
super(application);
mRepository = MenusRepository.getInstance(application);
restMenuData = mRepository.getMenus();
}
public LiveData<MenuResponse> getMenus() {
return restMenuData;
}
}
MenusRepository
private MenusRepository(Context context) {
apiRequest= RetrofitInstance.getInstance(context).getApiRequest();
}
public synchronized static MenusRepository getInstance(Context context) {
if (projectRepository == null) {
projectRepository = new MenusRepository(context);
}
return projectRepository;
}
public MutableLiveData<MenuResponse> getMenus() {
final MutableLiveData<MenuResponse> data = new MutableLiveData<>();
apiRequest.getMenus().enqueue(new Callback<MenuResponse>() {
#Override
public void onResponse(#NonNull Call<MenuResponse> call, #NonNull Response<MenuResponse> response) {
if (response.isSuccessful() && response.body() != null) {
data.setValue(response.body());
}
}
#Override
public void onFailure(#NonNull Call<MenuResponse> call, #NonNull Throwable t) {
data.setValue(null);
}
});
return data;
}
SecondActivity:
MenuViewModel mViewModel = ViewModelProviders.of(Objects.requireNonNull(SecondActivity.this)).get(MenuViewModel.class);
mViewModel.getMenus();
// This line not refresh menus
I except to refresh data from viewmodel, but it return old data.
How can I refresh viewmodel data in best practices?
MenusRepository.getMenus() methods creates a new instance of LiveData for every call. This is not the correct way to go about it.
You should have only one instance of LiveData and different objects subscribe to it (activity, viewModel, etc).
What you could do is - create a singleton of MenusRepository (which I think you have already done). Create one instance of MutableLiveData only and use it to update the data.
class MenusRepository {
private val liveData = MutableLiveData<MenuResponse>()
fun getMenus() {
// Your api call. Do not create a new instance of the livedata.
}
fun menus(): LiveData<MenuResponse> {
return liveData
}
fun update(data: MenuResponse) {
liveData.post(data)
}
This code is in Kotlin, but it applies similarly to Java as well.
You can update method to post an update to liveData. When you update it, all the observers will receive the new data. Use MenusRepository.menus() to access LiveData in your ViewModel.
Update
Your MenuRepository class could be like this.
private final MutableLiveData<MenuResponse> liveData = new MutableData<>();
private MenusRepository(Context context) {
apiRequest= RetrofitInstance.getInstance(context).getApiRequest();
}
public synchronized static MenusRepository getInstance(Context context) {
if (projectRepository == null) {
projectRepository = new MenusRepository(context);
}
return projectRepository;
}
public MutableLiveData<MenuResponse> loadMenus() {
apiRequest.getMenus().enqueue(new Callback<MenuResponse>() {
#Override
public void onResponse(#NonNull Call<MenuResponse> call, #NonNull Response<MenuResponse> response) {
if (response.isSuccessful() && response.body() != null) {
liveData.setValue(response.body());
}
}
#Override
public void onFailure(#NonNull Call<MenuResponse> call, #NonNull Throwable t) {
liveData.setValue(null);
}
});
}
public LiveData<MenuResponse> getMenus() {
return liveData;
}
public void updateData(response: MenuResponse) {
liveData.postValue(response);
}
When you want to update the data manually (from another activity),
use menuRepository.update() method. This will post the data to your LiveData which will update all its observers, ie. the ViewModel.
Call menuRepository.loadMenu() when you want to get the data using API.
Use menuRepository.getMenus() to get the LiveData and attach your observers.
Since MenuRepository is a singleton, there's only one instance of LiveData. When you will post an update to this instance of LiveData, all the observers will receive the new data.
public class MenuViewModel extends AndroidViewModel {
private MutableLiveData<MenuResponse> restMenuData;
private MenusRepository mRepository;
public MainActivityViewModel(#NonNull Application application) {
super(application);
mRepository = MenusRepository.getInstance(application);
restMenuData = mRepository.getMenus();
}
public LiveData<MenuResponse> getMenus() {
restMenuData = new MutableLiveData<>();
return restMenuData;
}
}
Change the View model code as above.
So that the live data is always cleared before returning to activity.