Make AsyncAppData in Kinvey serial - android

I want to get a data from a kinvey Collection, put it in a db and then modify it locally.I want to be sure that all data is gotten from collection before any modification. My problem is that, getting collection is done via AsyncAppData which runs in background,which makes my task impossible.
A sample code is shown
//getting data
ArrayList<String> runfunc = new ArrayList<String>();
final AsyncAppData<EventEntityWhy> myevents4 = mKinveyClient.appData("WhyWorldTemp", EventEntityWhy.class);
myevents4.get(new KinveyListCallback<EventEntityWhy>() {
#Override
public void onSuccess(EventEntityWhy[]){
for (EventEntityWhy x1 : result) {
String temp1 = (String) x1.get("whyindex");
runfunc.add(temp1)
}
}
}
//then processing will start
//runfunc array will be processed here

That's not how asynchronous programming works, and Kinvey has no plans to change that paradigm. The proper way to build your code is by processing your data inside the onSuccess callback rather than on the mainthread.
What you could do is set an event flag inside the callback, and then you create a waiter at your "//then processing will start" point that waits for that flag.

Related

ViewModel not updating the view on postValue

I used the lifecycle callback onCreate to fetch data like below
mWeOutViewModel.getPlaceListLiveData()
.observe(this, weOutItemViewModels -> {
AppLogger.i(getCustomTag() + "adding items " + weOutItemViewModels.size());
if (weOutItemViewModels != null && weOutItemViewModels.size() > 0)
mWeOutListAdapter.addToExisting(weOutItemViewModels);
});
As you can see the AppLogger output the initial size which is 0 when the fragment is displayed, then I fetch the data and call postValue (setValue crashes the app and it expected because I fetch data from the internet using a background thread). So I call post value like below :
private void updatePlaces(List<WeOutGroupedViewModels> weOutGroupedViewModels) {
List<WeOutGroupedViewModels> oldList = placeMutableLiveData.getValue();
oldList.addAll(weOutGroupedViewModels);
AppLogger.i(TAG +" updating places "+oldList.size());
placeMutableLiveData.postValue(oldList);
}
As you can see the other AppLogger before postValue, the size of the list is displayed(not empty), but nothing happens until the app crashes and nothing is shown in the logs. I have no ways of debugging since even on debug mode nothing happens. The post value doesn't trigger the observer.
I initialize the mutableLivedata like this :
private final MutableLiveData<List<WeOutGroupedViewModels>> placeMutableLiveData = new MutableLiveData<>();
and access like this :
public LiveData<List<WeOutGroupedViewModels>> getPlaceListLiveData() {
return placeMutableLiveData;
}
Event when I make the livedata public to access directly the livedata, there is no change (just in case someone thinks that's is where the issue comes from)
Instead of placeMutableLiveData.postValue(oldList);
I recommend using
placeMutableLiveData.postValue(Collections.unmodifiableList(new ArrayList<>(newList));
That way, the next time you access this list, you won't be able to mutate it in place, which is a good thing. You're not supposed to mutate the list inside a reactive state holder (MutableLiveData).
So theoretically it should look like this:
private void updatePlaces(List<WeOutGroupedViewModels> weOutGroupedViewModels) {
List<WeOutGroupedViewModels> newList = new ArrayList<>(placeMutableLiveData.getValue());
newList.addAll(weOutGroupedViewModels);
AppLogger.i(TAG +" updating places "+newList.size());
placeMutableLiveData.postValue(Collections.unmodifiableList(newList));
}

Best approach to waiting on pending live data using architecture components

I'm working on an application that fetches data from a graphql server via apollo-android.
I do a single fetch on my aws rds database. I do this fetch right at the onCreate() of my CalendarFragment.
The thing is, at onViewCreated(), I want to set my textview to one of the fields that is fetched, first and last name. So, I run my getBarberFullName method which returns the String value of mBarberFullName. I'm trying to follow the UI controller displays while the view model handles all the logic approach. getBarberFullName resides within my ViewModel.
public String getBarberFullName() {
if (appointmentsAreNull()) return mBarberFullName.getValue();
AppointmentModel am = mMasterAppointments.getValue().get(0);
String fullName = am.bFirstName;
fullName = fullName.concat(" " + am.bLastName);
// Get the logged in barber's full name and set it as mBarberFullName.
mBarberFullName.setValue(fullName);
return mBarberFullName.getValue();
}
where mMasterAppointments is a MutableLiveData<List<AppointmentModel>>. In my onViewCreated() callback, I run
String barberName = mBarberViewModel.getBarberFullName();
mTxtv_barberName.setText(barberName);
However, mMasterAppointments is always null so it just returns the default value of mBarberFullName which is a String.
However, if I were to run the following code, in the same onViewCreated(), I get the desired result where the textview is updated with the desired barber's full name.
mBarberViewModel.getAllAppointments().observe(getViewLifecycleOwner(), am -> {
if (am.isEmpty()) {
Log.d(TAG, "No barber.");
return;
}
String barberGreeting;
barberGreeting = am.get(0).bFirstName;
barberGreeting = barberGreeting.concat(" " + am.get(0).bLastName);
mTxtv_barberName.setText(barberGreeting);
});
getAllAppointments returns an observer to mMasterAppointments located in my ViewModel.
Although getAllAppointments and getBarberFullName are called within onViewCreated(), one is able to access the pending values of mMasterAppointments while the other is not. Why?
I don't want to do the logic in my Fragments onViewCreated callback, so how can I wait on the pending mMasterApointmentData in my ViewModel's getBarberFullName()? Are there tools within LiveData and ViewModel that would aid me in this situation?
Use LiveData's Transformations class
when you need to perform calculations, display only a subset of the
data, or change the rendition of the data.
First add a new String LiveData for BarberFullName in the viewmdoel, and give it the value of transforming (mapping) the source LiveData mMasterAppointments into the desired String:
val fullBarberName: LiveData<String> = Transformations.map(mMasterAppointments) { am ->
" ${am[0].bFirstName} ${am.get(0).bLastName}"
}
Now you can observe this String LiveData in your fragment, the way you in did your second snippet.
Note that the code I provided is in Kotlin, I use it nowadays. I hope you get it.

Receiving notification once all read operations are successfully performed

I have a question about some functionalities of Firestore. I am trying to occupy an array of data obtained through multiple read operations from the Firestore database. Is there a way for me to be notified when all the data are successfully read and stored in my array? This is particularly an issue because read operations are not finished in the order that they are called. Here are some code that illustrates my problem:
/* My array to insert the data read from the Firestore database */
String[] my_array = new String[3];
/* A method that will be called to initialize our array */
private void initArray(String doc_one, String doc_two, String doc_three) {
initSingleIndex(0, doc_one);
initSingleIndex(1, doc_two);
initSingleIndex(2, doc_three);
}
private void initSingleIndex(final int index, String doc_id) {
/* We perform our read operation here */
question_ref.document(doc_id).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
my_array[index] = documentSnapshot.getString("some_field");
}
});
}
My current implementation is to keep a global counter, which will be used to keep track of how many read operations were successfully carried out. I am also wondering whether the onSuccess() callbacks can be fired concurrently, since this will then lead to data corruption (i.e. the classic problem of incrementing values concurrently).
Any help or suggestion will be appreciated.

What is the best practice to chain realm queries in Android?

So, I have an Android app that uses realm.io. I have to run queries asynchronously like this :
public static void getProductsByCategoryId(Realm realm,
String categoryId,
OrderedRealmCollectionChangeListener<RealmResults<Product>> callback) {
RealmResults<Product> result = realm.where(Product.class)
.equalTo(CATEGORY, categoryId)
.findAllAsync();
result.addChangeListener(callback);
}
The callback will process this response, but then I need to run another query in sequence. So, you'll have queryA => process response => queryB => process response. So, the callback may have code like this
.....
getProductsByCategoryId(app.getRealmInstance(), "ABC123", firstCallback);
.....
private OrderedRealmCollectionChangeListener<RealmResults<Product>> firstCallback = new OrderedRealmCollectionChangeListener<RealmResults<Product>>() {
#Override
public void onChange(RealmResults<Product> realmProducts, OrderedCollectionChangeSet changeSet) {
mProdList.addAll(mRealm.copyFromRealm(realmProducts));
// get more product info (2nd call)
MainApplication.getMoreProductInfo(mRealm, mCatId, false, secondCallback);
}
};
Currently, my understanding is that you would run queryB in the callback of queryA ? Looking at the requirements for the app, I will end up with chains of 3 or 4 queries. Is this an appropriate approach, or is there a specific pattern I should be using ? I haven't found any guidance yet in the Realm documentation.
It's generally an indication of bad schema design if you need to do multiple queries in order to retrieve your result set, because the way Realm works is that if you can define your query results with one query (and you don't use realm.copyFromRealm() which you generally don't need to use anyways), then its elements and the results itself are all lazy-loaded.
If you cannot accomplish that, then even then, generally you probably shouldn't chain find*Async calls, because any RealmResults that you don't store as a field variable has a chance of being consumed by GC, and its change listener won't be called when isLoaded() is true (because said RealmResults no longer exists).
So what you really seem to want to do is just execute multiple queries on a background thread then return copied results to the main thread, in which case it'd just look like this
Executor executor = Executors.newSingleThreadedPool(); // or some other pool
Handler handler = new Handler(Looper.getMainLooper());
public void getQueryResults(DataLoadedCallback callback) {
executor.execute(() -> {
try(Realm realm = Realm.getDefaultInstance()) {
realm.refresh(); // <-- might not be necessary
RealmResults<XYZ> results1 = realm.where(XYZ.class)./*...*/.findAll();
RealmResults<ZXY> results2 = realm.where(ZXY.class)./*...*/.findAll();
RealmResults<YZX> results3 = realm.where(YZX.class)./*...*/.findAll();
List<Something> someList = new LinkedList<>();
for/*do magic transform things*/
someList.add(blah /* blah is not a managed RealmObject */);
}
handler.post(() -> {
callback.onDataLoaded(Collections.unmodifiableList(new ArrayList<>(someList)));
});
}
});
}
Chaining queries in the callbacks are fine and "should just work", but it would be far more efficient if you can express what you want is as few queries as possible.
Ideally, we should have a query language that is powerful enough to express everything you want in one query. We are not fully there yet, but we would be very interested to hear more about what specific requirements you have.
Also, it isn't clear why you are using copyFromRealm in the method you posted, but in an ideal situation that shouldn't be necessary.

UI responesive in Android

I create an app that like dictionary app. When the user types in an Edittext, I call an AsyncTask to compute and update the result to the screen (I put the UI update code in onPostExecute() method ). However, when you type little fast, the eddittext become not responesive (a little latency). I think this promblem occurs because many AsyncTasks are running (each AsynTask for an input letter). So, I think I need to stop the first task before calling new task. Am I right? What should I do in this situation?
You don't need to implement the filter method in an async task. I call filter method on data when first letter has been written in editbox and save the result in an temporary array, then when another letter has been written, I call filter method on the temporary data which technically has less information than the original data. By doing this, the dimmension of data set decreases as you type in editbox. Also, you can use this method to store previous data set so when you press backspace, you don't have to call filter method again, you just go to previous saved temporary data set. For me, it works fine and I don't have to use async task because it is efficient
I suggest you another approach: use only one thread. The searching thread should wait for searching data > do search > and sleep until new data. E.g.:
private static class SearchThread extends Thread{
private Object monitor = new Object();
private String value;
public void search(String value){
this.value = value;
synchronized (monitor){monitor.notify();}
}
#Override
public void run() {
while(true){
try {System.out.println("Wait for search data."); synchronized (monitor){monitor.wait(); }
} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("Searching for " + value);
}
}
}

Categories

Resources