Array operations sometimes throws 'Operation is invalid after previous operation' - android

In one of my parse subclasses I have a method like this:
public void updateCheckpoint(String checkpoint, boolean checked) {
if (checked) {
addUnique(checkedCheckpoints, checkpoint);
} else {
removeAll(checkedCheckpoints, Arrays.asList(checkpoint));
}
}
This update is immediately followed by a pinning to the local datastore.
Even when adding a ProgressDialog to ensure that the pinning has completed between updates, then sometimes the Operation is invalid after previous operation is thrown.
Is there a more robust way of doing array operations?

In the Parse documentation under arrays it says
"Note that it is not currently possible to atomically add and remove items from an array in the same save. You will have to call save in between every different kind of array operation."
When you both add and remove from your parse object, it will throw the error "Operation is invalid after previous operation". To avoid this error first add the new values to the array, save the object, remove the old values, then save again.

Initially, the value for an array on the dashboard reads (undefined). If you manually change it to (null) and try to add to it. It will throw this error.

Related

Data stored inside JSON object in Android is not available in the variable outside of it

I am trying to store the data I receive from the API call in the variable "data". However, the value of data is only updated inside the jsonObjectRequest and it is empty outside of it. How can I make it so that the value of data is available outside the jsonObjectRequest as well?(So I don't have to use the if(i==4) statement)
D/Inside Call: [https://i.redd.it/4tb9k1ray1f91.jpg, https://i.redd.it/4tb9k1ray1f91.jpg, https://i.redd.it/4tb9k1ray1f91.jpg, https://i.redd.it/4tb9k1ray1f91.jpg, https://i.redd.it/yftkun6o43f91.gif]
D/Outside Call: []
val data:ArrayList<String> = ArrayList()
for(i in 0 until 5){
val jsonObjectRequest=JsonObjectRequest(
Request.Method.GET,url,null,{
val value:String=it.getString("url")
data.add(value)
if(i==4) {
mAdapter.updateData(data)
}
Log.d("Inside Call",data.toString())
},{}
)
MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest)
}
Log.d("Outside Call",data.toString())
Your assumption of "inside" and "outside" calls is wrong. What you call the inside code is a callback that is, as the name suggests, called when your app has obtained the response to your request. The outside call is executed as soon as you've sent your requests, which is likely to be prior to receiving the responses.
Treat the "inside" code as the "outside" code and you may get closer to your aim. It is the code that will be called when you're "ready" to update your views, in simple terms.
Your i == 4 check is also error prone because it is not guaranteed that you'll get responses to your requests in the same order you've sent them. How you resolve this depends on your specific use case, so it's difficult to offer absolute solutions. You may be able to get away with using a counter that is incremented in the callback each time a response is obtained, and check if the counter has hit its max value (i.e., all responses have been obtained).

Firebase QuerySnapshot returning null for compounded queries

For my app, I have implemented a filter option. I use a Firebase Query to query using said filters. Here's the implementation:
private void loadDestinations() {
Query query = App.getFirestore().collection("destinations");
if (mFilters.hasCountry()) {
query = query.whereEqualTo(Destination.FIELD_COUNTRY, mFilters.getCountry());
}
if (mFilters.hasPrice()) {
query = query.whereEqualTo(Destination.FIELD_PRICE, mFilters.getPrice());
}
if (mFilters.hasSortBy()) {
query = query.orderBy(mFilters.getSortBy(), mFilters.getSortDirection());
}
query.addSnapshotListener((documentSnapshots, e) -> {
if (documentSnapshots != null) {
if (!documentSnapshots.isEmpty()) {
Log.d(LOG_TAG, "Snapshots are not null and have value");
List<Destination> destinationList = documentSnapshots.toObjects(Destination.class);
mDestinations.setValue(destinationList);
} else {
Log.d(LOG_TAG, "Snapshots are not null but have no value");
mDestinations.setValue(new ArrayList<>());
}
} else {
Log.e(LOG_TAG, "Snapshots are NULL!");
mDestinations.setValue(new ArrayList<>());
}
});
}
Take a look at the second line where I instantiate the query object. I then compound it for every type of filter (country, price, sorting) that was added by the user.
Now this loads perfectly fine if I compound country and price, but as soon as I add the sorting aka query.orderBy(), the documentSnapshots on line 17 return null. To make it even weirder, the snapshotListener retrieves data TWICE if it was with a compounded query. The first time it logs:
Snapshots are not null and have value
and then immediately afterward it logs
Snapshots are NULL!
even though the loadDestinations() method was called once (I checked).
I know for certain that this wasn't an issue a month or so ago because that's when I developed this portion and tested it out. Yet now, for reason, it is acting this way. Have there been any drastic changes to the API? Is the code at fault?
EXTRA: Say, on line 2, I add another .whereEqualTo() like so:
Query query = App.getFirestore().collection("destinations").whereEqualTo("approved", true);
The listener would return null values with just one filter added (as opposed to two before). The reason I mention this is because I don't think it's an issue with how the data is sorted but more so the fact that compounded queries don't work. And it, for some reason, retries a second time until it is null. I don't think it's about creating an index either because I already did that a month ago. Plus it would have mentioned as much in the log.
I suspect what is happening is that doing a "equals to" query on one field plus a "sort by" on another field requires your creating a composite index in order to run this query.
Normally, you'd get the URL to create this custom index in the error object, so I would highly recommend checking for, and then logging the contents of the error object.
(And even if that isn't the cause, it's always a good idea to get into the habit of checking your error object anyway. That'll probably help you avoid a bunch of mysterious bugs in the future.)

Storing null values in realm using Android

Previously, when there was no stable version of realm for Java (Android), we could not store null values in realm and we had to perform some unnatural hack to be able to do so, as explained in this post.
But as of now realm 1.0 is released, are there any update about being able to store null value?
For example : unfortunate cases when there is no field in JSON which I want to store in realm after parsing but haven't handled it manually.
I have the following code:
realmObject.setData(jsonObject.getString("SELECTOR"));
the program flow stops and exits the block the code is located inside.
the logcat shows
W/System.err: org.json.JSONException: No value for SELECTOR
But when I do:
realmObject.setData(null);
The program flow does not stop and continues across my realm statement realmObject.setData(null);
In some cases, there is no value for the tag "SELECTOR" in my Json file.
And I want it to be null in default.
I actually found out that the problem is actually with just :
jsonObject.getString("SELECTOR")
not the whole statement:
realmObject.setData(jsonObject.getString("SELECTOR"));
so the fix for me was
realmObject.setData(jsonObject.optString("SELECTOR"));
you can use has that will check whether key is available of not and basis of that save value to realm object
if (jsonObject.has("SELECTOR")) {
realmObject.setData(jsonObject.getString("SELECTOR"));
}
else{
realmObject.setData(null);
}

Local datastore write methods do not return (e.g..: pin(), pinAll(), unpin(), unpinAll())

I'm building an android app using the Android Parse SDK, which gets all data from Parse at initialisation and stores it locally. Later, it will only update those entities (ParseObjects) which need so. I'm not getting any return from some Pin() operations, and similarly no callback when I use PinInBackground() and variants. Same happens with Unpin().
My code is something like the following. I have a list of ControlDate, a ParseObject which contains updated_at and updated_locally_at for each Parse data table. I use it to decide if I should query a given table (reducing number of queries). I iterate over this list when I perform a data update, in an IntentService, like this:
protected void onHandleIntent(Intent intent) {
for(ControlDate d : mUpdateQueue) { // mUpdateQueue is the list of ControlDate
if(d.entityNeedsUpdate()) { // returns true if updated_at > updated_locally_at
updateEntity(d);
}
}
private boolean updateEntity(ControlDate d) {
String tableName = d.getTableName();
Date lastLocalUpdate = d.getLastLocalUpdate();
ParseQuery<ParseObject> qParse = ParseQuery.getQuery(tableName);
qParse.whereGreaterThan("updated_at", lastLocalUpdate);
try {
// update the entities
List<ParseObject> entities = qParse.find();
ParseObject.pinAll(entities); // SOMETIMES GETS STUCK (no return)
// update the associated ControlDate
d.setLastLocalUpdate(new Date()); // updated_locally_at = now
d.pin(); // SOMETIMES GETS STUCK (no return)
}
catch(ParseException e) {
// error
}
}
}
Those operations SOMETIMES do not return. I'm trying to find a pattern but still no luck, apparently it started happening when I added pointer arrays to some of the entities. Thus, I think it may be due to the recursive nature of pin(). However it is strange that it sometimes also gets stuck with ParseObjects which do not reference any others - as it is the case with d.pin().
Things I've tried:
changing the for loop to a ListIterator (as I am changing the list of ControlDates, but I don't think this is necessary);
using the threaded variants (eg.: PinInBackground()) - no callback;
pinning each entity individually (in a loop, doing pin()) - a lot slower, still got stuck;
debugging - the thread just blocks here: http://i.imgur.com/oBDjpCw.png?1
I'm going crazy with this, help would be much appreciated!
PS.: I found this https://github.com/BoltsFramework/Bolts-Android/issues/48
Its an open issue on the bolts library, which is used in the Android SDK and may be causing this (maybe?). Anyway I cannot see how I could overcome my problem even though the cause for the pin() not returning could be an "unobserved exception" leading to a deadlock.

Android and Azure Mobile Services: Using invokeAPI to return recordset

I am trying something very simple. I have a custom API called "missingvehiclesfrominventoryjob" and it simply returns a record set from an standard SQL Query.
I can do this in my WinForms and Windows Phone app easily but I cannot figure out how to do this on the Android App.
Here is my code: (which DOES NOT COMPILE in Android Studio):
msClient.invokeApi("missingvehiclesfrominventoryjob", kd, new
ApiOperationCallback<List<InventoryProspects>>(){
#Override
public void onCompleted(List<InventoryProspects> missingVehicles, Exception e,
ServiceFilterResponse serviceFilterResponse){
for (InventoryProspects item : missingVehicles){
mAdapter.add(item);
}
}
});
The problem is the List in the parameters of the Callback. I am not sure how to indicate that the invoiceAPI call will return multiple rows from the database and I cannot find anywhere in the docs to explain how. Nor can I find an example ANYWHERE on the internet.
I am sure I am not the only on trying to do this.
Thanks in advance
Chuck Giddens
What i did to overcome this problem, is to call a different overload of invokeApi that returns a JsonElement, and then deserialise it into my objects like so:
mClient.invokeApi("MyCustomApi",null, "GET", null, new ApiJsonOperationCallback() {
#Override
public void onCompleted(JsonElement jsonElement, Exception e, ServiceFilterResponse serviceFilterResponse) {
GsonBuilder gsonb = new GsonBuilder();
Gson gson = gsonb.create();
JsonArray array = jsonElement.getAsJsonArray();
List<MyObject> myObjects = new ArrayList<MyObject>()>
for(int i = 0; i < array.size(); i++)
{
myObjects.add(gson.fromJson(array.get(i).getAsJsonObject().toString(), MyObject.class));
}
}
});
I haven't had a chance to test it yet (will try when I have time and edit answer as needed) but my thinking is that the Android SDK won't allow you to do what you're trying to do. The invokeApi methods expect a strongly typed class to be set as the response type (or you can use the raw JSON methods). In this case, you're trying to say you want a list of items back, but I don't think that will work. I think you'll instead need to create a new class (i.e. missingvehiclesfrominventoryjobResponse) which contains a property that is of type List< InventoryProspects>. Note that you'll need to change your method call to actually match one of the available options for invokeApi which I don't believe it's doing right now. You can read more about the different formats of the method here: http://blogs.msdn.com/b/carlosfigueira/archive/2013/06/19/custom-api-in-azure-mobile-services-client-sdks.aspx
Alternatively, you can use the table methods against a table endpoint where the read expects a collection of results back.
Have you tried to remote debug your API call from the app.[http://blogs.msdn.com/b/azuremobile/archive/2014/03/14/debugging-net-backend-in-visual-studio.aspx]. Your app will timed out in doing that but you can see line by line execution of your controller action if it returns the correct result set. If there is no problem with it then the problem should be in parsing result set.
What is the exception you are getting in callback? And have you tried using other method parameters such as passing with different HTTP methods? Use this as a reference as well. http://azure.microsoft.com/en-us/documentation/articles/mobile-services-android-get-started/
Please paste your exception or either controller action, and the object structure of the data transfer object of the result set.

Categories

Resources