Post multiple MutableLiveData have no order - android

I'm using multiple MutableLiveData on an MVVM architecture.
on the ViewModel, I post the objects but the fragment is not resumed.
when the fragment has resumed the observers get the MutableLiveData but not in the order I post them.
How can I force an order of getting the MutableLiveData?
ViewModel:
void foo(){
first_MutableLiveData.post(newData)
second_MutableLiveData.post(newData)
}
fragment:
initView(){
first_MutableLiveData.observe(this,()->{
"getting called second"})
second_MutableLiveData.observe(this,()->{
"getting called first"})
}

You can't force what you want. As you can see from the code they are posting the result to the MainThread by calling:
ArchTaskExecutor.getInstance()
So now one would bother to support the syncronization between two different LiveData objects. It is your job do do so. It is a corner case.
Just use setValue, instead of postValue directly on the MainThread. Here is an example.
public class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
#Override
public void execute(Runnable runnable) {
handler.post(runnable);
}
}
public class YourClass {
MutableLiveData first_MutableLiveData = new MutableLiveData<Data>();
MutableLiveData second_MutableLiveData = new MutableLiveData<Data>();
private final Executor executor;
public YourClass(Executor executor) {
this.executor = executor;
}
void foo(){
executor.execute(new Runnable(){
#Override
public void run() {
first_MutableLiveData.setValue(newData);
second_MutableLiveData.setValue(newData);
}
});
}
}

So apparently when I changed the observer's order on the fragment they arrived in the order I needed them to be. Thanks everyone for the quick response!

Related

CountDownTimer : In Activity, ViewModel or separate class?

I would like to create a CountdownTimer which will trigger events that will update the UI (trigger popup, start an animation, etc.).
I wonder how to do this clean, here are my hypothesis and why :
A separate component EventCountdownTimer. I could then benefit the use of LifecycleObserver, but I wonder how to communicate the information back to the activity (I tried extending CountdownTimer and using it in the activity but I have an error and can't get it to compile)
In the Activity itself, it's the simplest but I'm not sure it belongs there as it isn't a UI component and I can't benefit the LifecycleObserver
In the ViewModel. I thought as it's activity related and the CountdownTimer is kinda logic data, it should go in here, but that means also watching the lifecycle of the activity, and holding any Activity related field within ViewModel is bad practice.
What's the best option according to you? And why?
In a MVVM pattern you could have a LiveData observable in your ViewModel which will be observed by the UI and upon value change you update the UI accordingly. How that observable changes value, that is your business logic and all of it should be in your ViewModel or in separate components that will be used by the ViewModel to update the observable state.
This will allow you to separate the UI from the business logic being your observable the bridge of communication between both, without the ViewModel having any knowledge of whats going on in the UI. In simple words it only executes what it is told to execute and updates a variable that is being observed, what then happens in the UI is the UI responsibility and with this you have reached a clear separation of concerns.
A separate component "EventCountdownTimer"
In my opinion, this is the best implementation that you might have in your case. For communicating information back to your activity, you might consider having an interface like the following.
public interface TimerListener {
void onTimerResponse(String response);
}
Modify your EventCountdownTimer to have a constructor which takes TimerListener as a parameter and override the onTimerResponse method in your activity. Now from your EventCountdownTimer, when you are trying to communicate with your activity along with a message, for example, you might just call the function onTimerResponse(msgToDeliver).
Hence your EventCountdownTimer should look something like this.
public class EventCountdownTimer {
public static Context context;
public static TimerListener listener;
public EventCountdownTimer(Context context, TimerListener listener) {
this.context = context;
this.listener = listener;
}
public startCountdown() {
// Start the count down here
// ... Other code
// When its time to post some update to your activity
listener.onTimerResponse(msgToDeliver);
}
}
And from your activity, initialize the EventCountdownTimer like the following.
EventCountdownTimer timer = new EventCountdownTimer(this, new TimerListener() {
#Override
public void onTimerResponse(String message) {
// Do something with the message data
// Update your UI maybe
}
});
I think you have provided good reasons already for not going for other options that you have mentioned.
Google solution : see it on github
/**
* A ViewModel used for the {#link ChronoActivity3}.
*/
public class LiveDataTimerViewModel extends ViewModel {
private static final int ONE_SECOND = 1000;
private MutableLiveData<Long> mElapsedTime = new MutableLiveData<>();
private long mInitialTime;
private final Timer timer;
public LiveDataTimerViewModel() {
mInitialTime = SystemClock.elapsedRealtime();
timer = new Timer();
// Update the elapsed time every second.
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
final long newValue = (SystemClock.elapsedRealtime() - mInitialTime) / 1000;
// setValue() cannot be called from a background thread so post to main thread.
mElapsedTime.postValue(newValue);
}
}, ONE_SECOND, ONE_SECOND);
}
public LiveData<Long> getElapsedTime() {
return mElapsedTime;
}
#Override
protected void onCleared() {
super.onCleared();
timer.cancel();
}
}

LiveData in Background Threads or non-UI components

Dears. I used to develop Android apps using MVP pattern and now I'm trying the MVVM with Architecture components like DataBind and LiveData.
I wrote my Repository class that provides a LiveData:
LiveData<MyEntity> getById(long id);
For Activity/Fragments I observe the LiveData exposed by ViewModel (that uses my Repository) and everything works fine.
The problem is I have to schedule an Alarm to display a Notification with a text related to MyEntity, so I created an Intent containing my MyEntityId as an Extra.
When the AlarmManager calls my BroadcastReceiver, I need to use the Repository to get MyEntity. The point is how to "Observe" the LiveData inside a non-UI component.
Also, I can start an IntentService (background thread) to avoid accessing the Repository in the Main Thread, and use something like "blockingNext" from RxJava, but I still could not figure a way to wait for LiveData.
What is the correct way of doing this? Note that my Repository may not be implemented using Room, due to legacy issues.
Thanks
The only solution I figured so far was have methods like this in the Repository:
LiveData<MyEntity> getByIdLive(long id);
MyEntity getById(long id);
But this does not look good for me.
So I'd like to ask how is the correct way of implement this.
Best Regards
It's better to avoid such things, but if you really need it and really know what you're doing you can try the following code:
public static <T> void observeOnce(final LiveData<T> liveData, final Observer<T> observer) {
liveData.observeForever(new Observer<T>() {
#Override
public void onChanged(T t) {
liveData.removeObserver(this);
observer.onChanged(t);
}
});
}
#WorkerThread
public static <T> T waitSync(final LiveData<T> liveData) {
final Object lock = new Object();
final Object[] result = new Object[1];
final boolean[] resultReady = new boolean[] {false};
(new Handler(Looper.getMainLooper())).post(new Runnable() {
#Override
public void run() {
observeOnce(liveData, new Observer<T>() {
#Override
public void onChanged(T t) {
synchronized (lock) {
result[0] = t;
resultReady[0] = true;
lock.notify();
}
}
});
}
});
synchronized (lock) {
try {
while (!resultReady[0]) {
lock.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
return null;
}
}
return (T) result[0];
}
You can only observe liveData in UI components like Activity/Fragment.
For your scenario, you can create an observer class which can be observed in non-UI classes as well or you can use EventBus.
to read about observer: https://developer.android.com/reference/java/util/Observer
about EventBus: https://github.com/greenrobot/EventBus

example of Runnable anonymous class mocking that applies changes in the Outterclass?

In my android project, In my presenter, I'm running one background thread in a Runnable anonymous class which applies changes in presenter class member. When the thread ends it calls a callback.
Does anyone have an example of Runnable anonymous class mocking that applies changes in the Outer class?
I was short on time so I couldn't publish my code. To answer my question I had to refactor my code and reduce the coupling between classes.
So, let me show you the conflictive part:
#Override
public void onResponse(final PresentationModel presentationModel) {
presentationModelWrapper.clear();
presentationModelWrapper.add(presentationModel);
if(isViewAttached()){
final SomeHandler someHandler = view.getSomeHandler();
someHandler.execute(new Runnable() {
#Override
public void run() {
//some code here
}
});
}
}
When the execution of someHandler ends I call the callback function which call a method of the presenter. Now, let me show you my solution:
#Mock
private SomeContract.View view;
#Mock
private SomeHandler someHandler;
#Captor
private ArgumentCaptor<SomeCallback> somethingCallbackArgumentCaptor;
#InjectMocks
private SomePresenter presenter;
#Test
public void loadSomething_shouldShowSomething() {
// Given
Map<String, String> parameters = new HashMap<String, String>();
parameters.put(Constant.API_PARAMETER_QUERY, CUSTOM_QUERY);
// Given a stubbed model
model = new PresentationModel();
//when
presenter.loadSomething(parameters);
//This part mock the someInteractor call
when(view.getSomeHandler()).thenReturn(someHandler);
verify(someInteractor).execute(anyMap(), somethingCallbackArgumentCaptor.capture());
somethingCallbackArgumentCaptor.getValue().onResponse(model);
//we are on response my conflictive part
verify(someHandler).execute(any(Runnable.class));
//I use presenter instead of ArgumentCaptor because handler has a presenter reference
presenter.onDatabaseOperationFinished();
// Then
SomethingUI uiModel = presenter.getUiModel();
verify(view).showSomething(uiModel);
}

Android - howto pass data to the Runnable in runOnUiThread?

I need to update some UI and do it inside of the UI thread by using runOnUiThread
Now the data for the UI comes from the other Thread, represented by data here.
How can i pass the data to the Runnable, so tht they can be used to update the UI?
Android doesn't seem to allow using data directly. Is there an elegant way to do this?
public void OnNewSensorData(Data data) {
runOnUiThread(new Runnable() {
public void run() {
//use data
}
});
}
My solution was creating a fioeld private Data sensordata inside of the runnable, and assigning data to it. This works only, if the original Data data is final.
public void OnNewSensorData(final Data data) {
runOnUiThread(new Runnable() {
private Data sensordata = data;
public void run() {
//use sensordata which is equal to data
}
});
}
The problem you found is that
Inner classes in Java capture ("close over") the lexical scope in which
they are defined. But they only capture variables that are declared "final".
If this is clear as mud, there's a good discussion of the details here:
Cannot refer to a non-final variable inside an inner class defined in a different method
But your solution looks fine. In addition, provided that data is final, you could simplify the code to this:
public void OnNewSensorData(final Data data) {
runOnUiThread(new Runnable() {
public void run() {
// use data here
data.doSomething();
}
});
}
If you want to avoid using an intermediate final variable (as described by Dan S), you can implement Runnable with an additional method to set Data:
public class MyRunnable implements Runnable {
private Data data;
public void setData(Data _data) {
this.data = _data;
}
public void run() {
// do whatever you want with data
}
}
You can then call the method like this:
public void OnNewSensorData(Data data) {
MyRunnable runnable = new MyRunnable();
runnable.setData(data);
runOnUiThread(runnable);
}
you could also make MyRunnable's constructor take in the Data instance as an argument:
public class MyRunnable implements Runnable {
private Data data;
public MyRunnable(Data _data) {
this.data = _data;
}
public void run() {
...
}
}
and then just say runOnUiThread(new MyRunnable(data));
I had a similar problem where I wanted to pass information into the thread. To solve it with the android system, I modifying corsiKa's answer in: Runnable with a parameter?
You can declare a class right in the method and pass the param as shown below:
void Foo(String str) {
class OneShotTask implements Runnable {
String str;
OneShotTask(String s) { str = s; }
public void run() {
someFunc(str);
}
}
runOnUiThread(new OneShotTask(str));
}
You'll need to update every time your program has new Data it wants to show. Your second code listing here is the standard way to accomplish this. There can be some catches if you're continuing to update Data in the thread. If this is the case consider blocking the thread until the UI finishes updating or copying the data to another Data object.
What's happening internally is that the JVM is copying the reference to the Data object for when the anonymous class will run. Data stored inside can still be changed. If your method requires additional changes to Data just use another variable (object reference) such as: final Data finalData = data;. You can also remove the line private Data sensordata = data; and use data directly in your run method.
It may not look elegant but this is the way Java passes object variables to anonymous classes. There is newer syntax in Java Language version 7 but Android is compatible with Java Language version 5 and 6.
Here is a typical case where service callback is called to update a UI status string (TextView textStatus). The service may be threaded.
The sample combines checking if thread redirection is needed and the actual redirection:
// service callbacks
public void service_StatusTextChanged(final String s) {
if( isOnUiThread() ) {
textStatus.setText(s);
} else {
runOnUiThread(new Runnable() {
#Override
public void run() {
textStatus.setText(s);
}
});
}
}
static public boolean isOnUiThread() {
return Thread.currentThread() == Looper.getMainLooper().getThread();
}
See also How to check if running on UI thread in Android?
public static Activity globalContext = null;
CommonSetting.globalContext = this;// put this in MainACtivity.onCreate()
public void createToastShort(final String message) {
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(CommonSetting.globalContext, message, Toast.LENGTH_SHORT).show();
}
});
}

Android how to runOnUiThread in other class?

In my application, in MainActivity, there is a thread which works fine. But when I call another class to get data from the server I can't run on a thread. See code example below.
class MainActivity extends Activity implements Runnable {
public void onCreate() {
new Thread(this).start();
}
public void run() {
//here is code for download data from server after completion this and in handler i m call other class in setdata() method....
}
public void setData() {
new CheckData(this);
}
}
class CheckData {
public CheckData(Context context) {
context.runUIonthread(){//cant call as runUIthread............
}
}
See the article Communicating with the UI Thread.
With Context in hand, you can create a Handler in any class. Otherwise, you can call Looper.getMainLooper(), either way, you get the Main UI thread.
For example:
class CheckData{
private final Handler handler;
public Checkdata(Context context){
handler = new Handler(context.getMainLooper());
}
public void someMethod() {
// Do work
runOnUiThread(new Runnable() {
#Override
public void run() {
// Code to run on UI thread
}
});
}
private void runOnUiThread(Runnable r) {
handler.post(r);
}
}
Here's a solution if you don't want to pass the context:
new Handler(Looper.getMainLooper()).post(new Runnable() {
public void run() {
// code goes here
}
});
Activity is a class that extends Context. So there is no need to pass both context and activity. You may pass activity as context and then you can use the context to run on UI thread as follows:
((Activity) context).runOnUiThread(new Runnable() {
public void run() {
//Code goes here
}
});
Word of Caution: Only use this when you're sure that context is an activity context, and it's not a good practice to assume that.
class MainActivity extends Activity implements Runnable{
public void oncreate(){
new Thread(this).start();
}
public void run(){
//here is code for download data from server after completion this and in handler i m call other class in setdata() method....
}
public void setdata();
{
new checkData(this,MainActivity.this);
}
}
class checkData{
public void checkdata(Context context,MainActivity mainactivity){
mainactivity.runUIonthread()..is works fine for me.....
}
}
You might want to take a look at AsyncTask. Even though it's not the best solution, it will help you get started.
http://developer.android.com/reference/android/os/AsyncTask.html
EDIT
I don't see why using an AsyncTask is not a solution for you but anyway. You can hold a Handler class that is initialized in the UI thread. Then using this Handler you can post back messages to the UI in the form of a runnable. So all you need to do is instantiate a new Handler object when you are in the UI thread (before you start your new one) and then share that with your other class. When you are done, you can use that instance to post a message back to the UI thread using the post method. Check out the documentation of the Handler class for more details:
http://developer.android.com/reference/android/os/Handler.html
If someone's looking for an Rx based solution:
Observable.just(true)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aBoolean -> {
// cool stuff comes here
});

Categories

Resources