I was making two Parse.com queries ..
ABC.findInBackground(new FindCallback<ParseObject>()
{
...
}
DEF.findInBackground(new FindCallback<ParseObject>()
{
...
}
and I essentially tried doing this,
CountDownLatch waitForBoth = new CountDownLatch(2);
ABC.findInBackground(new FindCallback<ParseObject>()
{
...
waitForBoth.countDown();
}
DEF.findInBackground(new FindCallback<ParseObject>()
{
...
waitForBoth.countDown();
}
// (error handling (just a flag) not shown)
waitForBoth.await();
Log("both are finished yay!");
of course, I just get the sundry errors IllegalMonitorStateException: object not locked by thread before wait() etc etc etc.
Part of the problem other than me being dense is, those calls to Parse's findInBackground in fact, take you off on a new thread anyway, right?
For the record, one of those Parse calls looks like this,
ParseQuery<ParseObject>PQ = ParseQuery.getQuery("SomeTable");
PQ.whereEqualTo("SomeColumn", ParseUser.getCurrentUser());
PQ.include("SomeOtherColumn");
PQ.findInBackground(new FindCallback<ParseObject>()
{
#Override
public void done(List<ParseObject> res, ParseException e)
{
if (e == null)
{
// res is the List of ParseObject results from the cloud
// here, you'd basically...
waitForBoth.countDown();
}
else
{
int errCodeSimple = e.getCode();
exampleWoeFlag = false;
waitForBoth.countDown();
}
}
});
}
and the doco on that ...
http://parse.com/docs/android/api/com/parse/ParseQuery.html#findInBackground(com.parse.FindCallback)
The Parse SDK does create a new Thread, but the "done" method is running on the UI Thread. This means you are calling "countDown" on the UI Thread, but you will never reach there, because you have called "await" on it.
You could solve it by creating your own Threads and passing the CountDownLatch in it. And then use Parse's synchronous methods
does that make sense ? Someone correct me if Im wrong
By the way, there is an excellent (free) course running on coursera at the moment (https://www.coursera.org/course/posa) that discusses the CountDownLatch in Android.
kind regards
Anton
Related
I have this query to update data already in my realm table;
for (MyGameEntrySquad squad : response.body().getSquad()) {
subscription = realm.where(RealmPlayer.class).equalTo("id", squad.getPlayer().getId())
.findFirstAsync()
.asObservable()
.subscribe(new Action1<RealmObject>() {
#Override
public void call(RealmObject realmObject) {
}
});
}
I would like to perform this query asynchronously then display the results on the UI.
Basically, whatever is been returned by response.body().getSquad() has an id matching a record already in the DB; and that is what am using in my equalTo method.
Based on the data received, I would like to update two columns on each of the record matching the IDs.
However, I am facing a few challenges on this:
The Action1 in subscribe is returning a RealmObject instead of a PlayerObject
How to proceed from here
Any guidance on this will be appreciated.
Thanks
Update
if (response.isSuccessful()) {
//asynchronously update the existing players records with my squad i.e is_selected
for (MyGameEntrySquad squad : response.body().getSquad()) {
realm.where(RealmPlayer.class).equalTo("id", squad.getPlayer().getId())
.findFirstAsync()
.<RealmPlayer>asObservable()
.filter(realmPlayer -> realmPlayer.isLoaded())
.subscribe(player -> {
realm.beginTransaction();
if (squad.getPlayer().getPosition().equals("GK")) {
player.setPlaygroundPosition("gk");
player.setIsSelected(true);
}
// pick the flex player
if (squad.isFlex()) {
player.setPlaygroundPosition("flex");
player.setIsSelected(true);
}
// pick the Goalie
if (squad.getPlayer().getPosition().equals("GK")) {
player.setPlaygroundPosition("gk");
player.setIsSelected(true);
}
// pick the DFs
if ((squad.getPlayer().getPosition().equals("DF")) && (!squad.isFlex())) {
int dfCounter = 1;
player.setPlaygroundPosition(String.format(Locale.ENGLISH, "df%d", dfCounter));
player.setIsSelected(true);
dfCounter++;
}
// pick the MFs
if ((squad.getPlayer().getPosition().equals("MF")) && (!squad.isFlex())) {
int mfCounter = 1;
player.setPlaygroundPosition(String.format(Locale.ENGLISH, "mf%d", mfCounter));
player.setIsSelected(true);
mfCounter++;
}
// pick the FWs
if ((squad.getPlayer().getPosition().equals("FW")) && (!squad.isFlex())) {
int fwCounter = 1;
player.setPlaygroundPosition(String.format(Locale.ENGLISH, "mf%d", fwCounter));
player.setIsSelected(true);
fwCounter++;
}
realm.copyToRealmOrUpdate(player);
realm.commitTransaction();
updateFieldPlayers();
});
}
hideProgressBar();
}
realm.where(RealmPlayer.class).equalTo("id", squad.getPlayer().getId())
.findFirstAsync()
.<RealmPlayer>asObservable()
.subscribe(new Action1<RealmPlayer>() {
#Override
public void call(RealmPlayer player) {
}
});
You should do like that.
Btw, it's bad idea to do it in a cycle - check in method of RealmQuery.
for (MyGameEntrySquad squad : response.body().getSquad()) { // btw why is this not `Observable.from()`?
subscription = realm.where(RealmPlayer.class).equalTo("id", squad.getPlayer().getId())
.findFirstAsync()
.asObservable()
This should not be on the UI thread. It should be on a background thread. On a background thread, you need to use synchronous query instead of async query.
Even on the UI thread, you'd still need to filter(RealmObject::isLoaded) because it's an asynchronous query, and in case of findFirstAsync() you need to filter for RealmObject::isValid as well.
For this case, you would not need asObservable() - this method is for observing a particular item and adding a RealmChangeListener to it. Considering this should be on a background thread with synchronous query, this would not be needed (non-looper background threads cannot be observed with RealmChangeListeners).
You should also unsubscribe from any subscription you create when necessary.
And yes, to obtain RealmPlayer in asObservable(), use .<RealmPlayer>asObservable().
In short, you should put that logic on a background thread, and listen for changes on the UI thread. Background thread logic must be done with the synchronous API. You will not need findFirstAsync for this.
In my app, I am synchronizing all my data via realm transactions in the background thread. However, occasionally when I try to retrieve a recently synchronized object in the UI thread, I get a null pointer exception. I have written a routine that seems to be a fix to this small latency problem, but it feels like a "hacky" solution. Any guidance would be greatly appreciated. I have posted a simplified version of the routine below.
private void attemptToEnterActivity(){
// Retrieve the object's key.
String key = Application.getInstance().getRecentlySyncedPrimaryKey();
// Retrieve the realm object with the key.
Realm realm = Realm.getDefaultInstance();
Object object = realm.where(Object.class)
.equalTo("key", key)
.findFirst();
if (object != null) {
new NavigationController(this).activity_start(new Intent(this, Activity.class));
} else {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
attemptToEnterActivity();
}
}, 200);
}
}
Instead of guessing when the object will be fetched and stored, the suggestion is to use RealmChangeListener. See doc here for more information.
In your case, you can also use Realm's async query like:
RealmResults<Object> results = realm.where(Object.class)
.equalTo("key", key).findAllAsync();
results.addChnageListener(newRealmChangeListener<RealmResults<Object>>() {
#Override
public void onChange(RealmResults<Object> objects) {
if (objects.size() > 0) {
Object object = objects.first();
// ....
}
}
});
I'm working on a chat app for Android, and I need to create several different conversations with one message in all of these created conversations. In my code, it looks like that :
for (ParseObject receiver : receivers) {
final ParseObject convQuery = ParseObject.create("Conversation");
convQuery.put("from", fromUser);
convQuery.put("to", receiver);
convQuery.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
ParseObject messageQuery = ParseObject.create("Message");
messageQuery.put("conversation", convQuery);
messageQuery.put("author", fromUser);
messageQuery.saveInBackground();
}
});
}
As you can see, I am executing multiple Parse queries in a for loop. I'm not sure if it's the good way to go... Will it work? Is it optimized? Or is there another better way to do what I want here?
Thanks!
i am making an android app in which i am getting the data from the server side by making doinbackground method and assigning these values to the variables which are declared globally. Problem is that i am using that variables in oncreate method where it give me null values because the doinbackground method finishes after the variables are used in oncreate. what could be the solution for this?
public void getProfileInfo() {
String currentUserId = ParseUser.getCurrentUser().getObjectId();
ParseQuery<ParseObject> query = ParseQuery
.getQuery(ParseConstants.KEY_USER_INFO);
query.whereEqualTo(ParseConstants.KEY_USER_ID_INFO, currentUserId);
query.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> objects, ParseException e) {
if (e == null) {
for (ParseObject thisUser : objects) {
name = thisUser
.getString(ParseConstants.TYPE_INFO_FULLNAME);
email = ParseUser.getCurrentUser().getEmail();
}
} else {
Log.d("Activity", "Error: " + e.getMessage());
}
}
});
}
i am calling this method in oncreate before i use the variables name and email.i checked the value of name and email. it gave me the right values.
It sounds like you have a bit of timing problem.
Just to state the obvious, you can't start an asynchronous task on a background thread, and then blindly use the results without knowing that the task has completed.
The use of the results needs to be triggered by the completion of the task.
So, whatever you're doing to access these 'global variables' that is getting nulls - don't do it at a deterministic time (e.g. in onCreate) - do it when you actually get the results back.
If there's a chance that the results may come back before the point where you're currently trying to access them, check to see if they have come back, and if they have, use them. If they haven't, wait until they do come back.
I'll leave the implementation of this as an "exercise for the reader"
The find on the db does not complete, so the FindCallback.done() method is never called, no errors, nothing, just is not called. this is the code
ParseQuery<ParseObject> pq = ParseQuery.getQuery("Category").setLimit(1);
pq.getFirstInBackground(new GetCallback<ParseObject>() {
#Override
public void done(ParseObject object, ParseException e) {
throw new RuntimeException();
}
});
I can see the prints before and after this portion of code, but the exception is never thrown. Please, I do really need help.
Edit: I use the parse-login before this point and it works correctly, even the saveInBackground() works correctly, the only problems I find are about queries, (find, getfirst ecc).
Resolved, I found out that the background features offered by Parse do not work if in your activity there are fragments, so you have to implement them yourself. The only strange fact is that in the beginning some function (such saveInBackground as I mentioned above) worked.
Check the ACL column in the parse object row, maybe you don't have permission to read.Double click on the ACL column and check the "public Read".
Thy this:
ParseQuery<ParseObject> pq = ParseQuery.getQuery("Category").setLimit(1);
pq.findInBackground(new FindCallback< ParseObject >() {
#Override
public void done(List< ParseObject > parseObjects, ParseException e) {
// Success
} else {
e.printStackTrace();
}
The pq.getFirstInBackground(new GetCallback<ParseObject>() is meant to return only one object, so you need to help it zero in on that object, for example a particular name or item.
Try adding pq.whereEqualTo("item", biscuit);.
If you want to return a list of objects you need to use a different callback.
End product should look like this.
ParseQuery<ParseObject> pq = ParseQuery.getQuery("Category").setLimit(1);
pq.whereEqualTo("item_key_here", "item_value_here");
pq.getFirstInBackground(new GetCallback<ParseObject>() {
#Override
public void done(ParseObject object, ParseException e) {
if(object != null){
//do stuff here
}
});