I'm unable to properly fetch a ParseObject that contains a field of type 'Object' : after changing manually the 'Object' field value in the Parse DataBrowser and then fetch the ParseObject from the app, the fetched ParseObject still provide the old value for the 'Object' field, but provide the right new value for the 'String' field.
Here is the sample code I use :
public class MainActivity extends ActionBarActivity {
ParseObject object;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
object = ParseObject.createWithoutData("Test", "tvgTg8jAXz");
}
#Override
protected void onResume() {
super.onResume();
object.fetchInBackground().onSuccess(new Continuation<ParseObject, Object>() {
#Override
public Object then(Task<ParseObject> task) throws Exception {
JSONObject data = task.getResult().getJSONObject("data");
String name = task.getResult().getString("name");
Log.d("OBJECT", data.toString());
Log.d("OBJECT", name);
return null;
}
}).continueWith(new Continuation<Object, Object>() {
#Override
public Object then(Task<Object> task) throws Exception {
if (task.getError() != null) {
Log.e("OBJECT", task.getError().getLocalizedMessage());
}
return null;
}
});
}
}
After I change both 'data' and 'name' fields in the DataBrowser, if 'onResume()' is called without a previous call to 'onCreate()' (after locking/unlocking screen for example) then the logs shows the old value for 'data' and the new value for 'name'.
This is a simple code example to highlight the problem I encounter in a bigger project.
Is this a known issue of the Parse Android SDK ? Is there a workaround ?
Thanks
Now that I learned that you have turned on the local datastore I can come with an, at least partial, answer.
Turning on the local datastore has some side effects. One being that only one instance of each object exists locally. So when you call fetchInBackground the second time, object is already populated with data. The problem then (i think) is that the API no longer override 'complex' types (pointers, objects, arrays), perhaps because it could mess up internal relationships in the data store. Since the fact that the data store will recursively save an object (and pointers) so suddenly swapping a pointer might leave objects 'hanging'. (again, only guessing).
Now I must admit that it still confuses me a bit looking at your code, cause it does not seem that you at any point write your object to the data store, however..
What should work is to unpin the object before 'refreshing' it:
object.unpinInBackground.onSuccess(new Continuation<>{
...
// when done call fetch
});
According to Parse, this is a known issue that they will not fix for now : https://developers.facebook.com/bugs/1624269784474093/
We must use the following methods to retrieve JSON objects/arrays fields from a ParseObject :
getMap() instead of getJSONObject()
getList() instead of getJSONArray()
These methods will return Map and List objects respectively.
I found that managing Map and List in my project instead of JSONObjet and JSONArray is not a problem and is even clearer.
Related
I am applying filters on realm using RealmResults<>.
I begin to do like this -
RealmResults<data> filteredRealmResults;
List<data> tranfilteredlist;
private OrderedRealmCollectionChangeListener<RealmResults<data>> filteredTransChangeListener =
new OrderedRealmCollectionChangeListener<RealmResults<data>>() {
#Override
public void onChange(RealmResults<data> results, OrderedCollectionChangeSet changeSet) {
Log.d("realm", "filteredRealmResults.size():" + filteredRealmResults.size());
tranfilteredlist = results;
initFilterAdapter();
}
};
Now I want to delete the filteredRealmResults. I did like this -
void deleteFilteredRealmResults() {
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
// Delete all matches
filteredRealmResults.deleteAllFromRealm();
}
});
}
After doing this my data in the realm got deleted. So I just try to delete the tranfilteredlist but it throws an exception that it does not support .clear();
I want to clear if from the memory whatever is holder the query data. Correct me if I am wrong or doesn't understand or just worrying too much.
I read This class holds all the matches of a RealmQuery for a given Realm. The objects are not copied from the Realm to the RealmResults list, but are just referenced from the RealmResult instead. This saves memory and increases speed.
I want to clear if from the memory whatever is holder the query data.
Correct me if I am wrong or doesn't understand or just worrying too
much.
Once you invoke filteredRealmResults.deleteAllFromRealm, it will clear the internal resultant elements object(which holds the elements) and as you know, resultant objects are reference so data will be deleted from realm database too. Hence, there is no need to call clear on the RealmResults object.
You can verify this by calling filteredRealmResults.size() after deletion, it will return 0.
I just try to delete the tranfilteredlist but it throws an exception
that it does not support .clear();
It is the expected behaviour as clear has been deprecated so don't use it.
Why deprecated?
deleteAllFromRealm automatically clears the list so no need to call it again explicitly.
Calling clear on RealmResults object will result in deletion of data from database, can cause unexpected behaviour if the user is not aware so API is being modified to avoid unexpected behaviours.
private String u_id;
private String u_name;
#Override
public void onSuccess(LoginResult loginResult) {
if(Profile.getCurrentProfile() == null) {
mProfileTracker = new ProfileTracker() {
#Override
public void onCurrentProfileChanged(Profile profile, Profile profile2) {
// profile2 is the new profile
u_id = profile2.getId().toString();
u_name = profile2.getName().toString();
mProfileTracker.stopTracking();
}
};
// no need to call startTracking() on mProfileTracker
// because it is called by its constructor, internally.
}
else {
Profile profile = Profile.getCurrentProfile();
u_id = profile.getId().toString();
u_name = profile.getName().toString();
}
/*new CreateNewProduct().execute();*/
/*updateFacebookButtonUI();*/
}
I want get value u_id and u_name to add arraylist, but it return null. I tried log have result. I need way resolve. Thanks :(
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("u_id", u_id)); // return null
params.add(new BasicNameValuePair("u_name", u_name)); // return null
Yes, adding "" results will not be null, but I want to get the value of the variable inside the onSuccess method to pass to the List below. The list below is in another class in same file with on Success
Variables u_id and u_name can be null only if they were not initialised. You don't set their value when adding the field to the class.
private String u_id; //only type and name set, not the value
I think if you're sure their value have to be set somewhere in the inner methods (doesn't matter in if or else either), it's not so dangerous to leave it like this. Also I would bet you're calling params.add(...)... after the above lines, unless it would cause the variables being null too...
But if you really want to get something back from your variables, you can initialise them as plain empty strings (""),
private String u_id = ""; //the variable's value is set too
and maybe later handle the "empty-string" checking before adding their value to your List<NameValuePair> variable; however it's not necessary. The main thing is: this way you can be 100% sure your variables won't be null...
EDIT #1: I don't know much about the structure of your file which contains onSuccess() method and the class with your list inside of it, but I would say it's another Java class file (MyClass.java or similar). I'm not an expert in Android multithreading, but I would say there is not anything that could block your inner class with your list to access the fields in the outer base class. You can reach fields anytime and anywhere in a class, no matter you want to do it in that very class or in the 10th nested inner class or method.
So unless your code does not look similar to this, I would think my first answer (above the edit) should be the solution. Maybe you should provide the code of your full class(es) to find the best solution, not just parts like you did first time.
This question is a follow-up question from: Organize Android Realm data in lists
Due to the data returned by the API we use, it's slightly impossible to do an actual query on the realm database. Instead I'm wrapping my ordered data in a RealmList and adding a #PrimaryKey public String id; to it.
So our realm data looks like:
public class ListPhoto extends RealmObject {
#PrimaryKey public String id;
public RealmList<Photo> list; // Photo contains String/int/boolean
}
which makes easy to write to and read from the Realm DB by simply using the API endpoint as the id.
So a typical query on it looks like:
realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();
This creates a slightly overhead of listening/subscribing to data because now I need to check listUser.isLoaded() use ListUser to addChangeListener/removeChangeListener and ListUser.list as an actual data on my adapter.
So my question is:
Is there a way I can query this realm to receive a RealmResults<Photo>. That way I could easily use this data in RealmRecyclerViewAdapter and use listeners directly on it.
Edit: to further clarify, I would like something like the following (I know this doesn't compile, it's just a pseudo-code on what I would like to achieve).
realm
.where(ListPhoto.class)
.equalTo("id", id)
.findFirstAsync() // get a results of that photo list
.where(Photo.class)
.getField("list")
.findAllAsync(); // get the field "list" into a `RealmResults<Photo>`
edit final code: considering it's not possible ATM to do it directly on queries, my final solution was to simply have an adapter that checks data and subscribe if needed. Code below:
public abstract class RealmAdapter
<T extends RealmModel,
VH extends RecyclerView.ViewHolder>
extends RealmRecyclerViewAdapter<T, VH>
implements RealmChangeListener<RealmModel> {
public RealmAdapter(Context context, OrderedRealmCollection data, RealmObject realmObject) {
super(context, data, true);
if (data == null) {
realmObject.addChangeListener(this);
}
}
#Override public void onChange(RealmModel element) {
RealmList list = null;
try {
// accessing the `getter` from the generated class
// because it can be list of Photo, User, Album, Comment, etc
// but the field name will always be `list` so the generated will always be realmGet$list
list = (RealmList) element.getClass().getMethod("realmGet$list").invoke(element);
} catch (Exception e) {
e.printStackTrace();
}
if (list != null) {
((RealmObject) element).removeChangeListener(this);
updateData(list);
}
}
}
First you query the ListPhoto, because it's async you have to register a listener for the results. Then in that listener you can query the result to get a RealmResult.
Something like this
final ListPhoto listPhoto = realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();
listPhoto.addChangeListener(new RealmChangeListener<RealmModel>() {
#Override
public void onChange(RealmModel element) {
RealmResults<Photo> photos = listPhoto.getList().where().findAll();
// do stuff with your photo results here.
// unregister the listener.
listPhoto.removeChangeListeners();
}
});
Note that you can actually query a RealmList. That's why we can call listPhoto.getList().where(). The where() just means "return all".
I cannot test it because I don't have your code. You may need to cast the element with ((ListPhoto) element).
I know you said you're not considering the option of using the synchronous API, but I still think it's worth noting that your problem would be solved like so:
RealmResults<Photo> results = realm.where(ListPhoto.class).equalTo("id", id).findFirst()
.getList().where().findAll();
EDIT: To be completely informative though, I cite the docs:
findFirstAsync
public E findFirstAsync()
Similar to findFirst() but runs asynchronously on a worker thread This method is only available from a Looper thread.
Returns: immediately an empty RealmObject.
Trying to access any field on the returned object before it is loaded
will throw an IllegalStateException.
Use RealmObject.isLoaded() to check if the object is fully loaded
or register a listener RealmObject.addChangeListener(io.realm.RealmChangeListener<E>) to be
notified when the query completes.
If no RealmObject was found after
the query completed, the returned RealmObject will have
RealmObject.isLoaded() set to true and RealmObject.isValid() set to
false.
So technically yes, you need to do the following:
private OrderedRealmCollection<Photo> photos = null;
//...
final ListPhoto listPhoto = realm.where(ListPhoto.class).equalTo("id", id).findFirstAsync();
listPhoto.addChangeListener(new RealmChangeListener<ListPhoto>() {
#Override
public void onChange(ListPhoto element) {
if(element.isValid()) {
realmRecyclerViewAdapter.updateData(element.list);
}
listPhoto.removeChangeListeners();
}
}
Using parse.com with android, I am retrieving a parseObject in a method and I want to update the same exact object in another method without having to retrieve it again. What is the best way to do this?
Here is my attempt:
I tried to save a copy of the object when first retrieved and change this copy and then save this copy using saveInBackground method. I highly doubt that this would work. The copy will be saved but the original object won't be saved, so what is the alternative?
method that retrieves the object:
query.getFirstInBackground(new GetCallback<ParseObject>() {
public void done(ParseObject object, ParseException e) {
MyShop = object;
}
method that updates the object:
public void OnClick(View arg0){
MyShop.put("status", false);
MyShop.saveInBackground();
}
Where MyShop is a global variable and I want to update the ParseObject object
It turned out that copies of the same ParseObject refer to the same object on cloud, meaning if one copy is updated the original copy is updated. It looks like all of them are references to the same object, maybe because this is Java
I am trying to restore an array of Objects from a savedInstanceState. I added each one to the Bundle individually here: (rhythm is the array of Objects)
#Override
public void onSaveInstanceState(Bundle outState){
outState.putInt("numParts",rhythm.length);
for(int index = 0;index<rhythm.length;++index){
outState.putSerializable(""+index,rhythm[index].beat);
}
super.onSaveInstanceState(outState);
}
When the onRestoreInstanceState() method is called, I try to assign my rhythm array with the Objects from the Instance State here: (it isn't null)
#Override
public void onRestoreInstanceState(Bundle savedInstanceState){
rhythm = new Part[savedInstanceState.getInt("numParts")];
for(int index = 0; index<rhythm.length;++index){
Object middleMan =savedInstanceState.getSerializable(""+index);
if(middleMan==null){
System.out.println("It's null...");
}
rhythm[index]=(Part) middleMan;
}
}
It throws a ClassCastException when I parse to a Part every time. Part implements Serializable. Why is it not allowing me to parse? Will I need to do custom serialization?
Please help!
I am guessing that Part is a type that you have created? So instead of treating Part as an array
rhythm = new Part[savedInstanceState.getInt("numParts")];
You want to instantiate a new Part object like so:
rhythm = new Part(savedInstanceState.getInt("numParts"));
Other assumptions:
rhythm is a member variable
The constructor for Part takes a single integer
Okay I just did it as the whole array and it worked... I don't really know why, but it did. Thanks for giving me the idea to just pass the whole array. #Error 454