Iteration out of sync - java

I am using Parse.com as my backend. I am trying to load an object in the background which works fine, but these objects have a pointer to an image. The problem is, that in order to query the images in the image class, i pass a ParseObject in order to get which object the image is pointing to like this:
ParseObject advertToGetImageFrom = ParseObject.createWithoutData("Advert", advertList.get(j).getObjectId());
However, my log shows this result
D/app: DT34J9zFKI
D/app: dAqVrnZ1rf
D/app: bNtIfOCqeE
D/app: Images loaded: 1
D/app: Images loaded: 1
D/app: Images loaded: 1
Which is reasonable since the loading is async. However, all the returned images are the same? Why is this happening? Here is the entire call:
for (int j = 0; j < titleMap.size(); j++){
ParseQuery<ParseObject> query2 = ParseQuery.getQuery("Image");
ParseObject advertToGetImageFrom = ParseObject.createWithoutData("Advert", objectList.get(j).getObjectId());
Log.d("app", advertToGetImageFrom.getObjectId());
query2.whereEqualTo("advertId", advertToGetImageFrom);
query2.findInBackground(new FindCallback<ParseObject>() {
public void done(final List<ParseObject> imageList, ParseException e) {
// commentList now has the comments for myPost
Log.d("app", "Images loaded: " + imageList.size());
if (imageList != null) {
ParseFile pFile = (ParseFile) imageList.get(0).get("image");
pFile.getDataInBackground(new GetDataCallback() {
public void done(byte[] data, ParseException e) {
if (e == null) {
Bitmap bmp = decodeFile(data);
ParseObject advertID = imageList.get(0).getParseObject("advertId");
imageMap.put(advertID.getObjectId(), bmp);
if (imageMap.size() == titleMap.size())
updateCardView();
} else {
Log.d("test", "There was a problem downloading the data.");
}
}
});
}
}
});
}

Not familliar with parse.com but I can hazard a guess.
I suspect your problem is with the line:
ParseObject advertID = imageList.get(0).getParseObject("advertId");
Here you are accessing the imageList that was provided to the done callback of your findInBackground invocation. The problem is that you are now in the done callback of a getDataInBackground invocation and although the imageList is final that does not make it immutable and has probably changed since the original call.
This doesn't seem like a terrible thing at first sight as this should only affect the advertID but think about that further and you may see the problem.
I think the solution could be to grab a local copy of the data that might change. Something like:
pFile.getDataInBackground(new GetDataCallback() {
// **** Grab the ParseObject befor it gets changed.
ParseObject theParseObject = imageList.get(0);
public void done(byte[] data, ParseException e) {
if (e == null) {
Bitmap bmp = decodeFile(data);
// **** Refer to the grabbed ParseObject not the changing outer one.
ParseObject advertID = theParseObject.getParseObject("advertId");
If that doesnt fix your issue then I feel certain it will be something like this.

Related

How to see when information is lost using API and Glide for images

I'm setting up a NewYorkTimes like news application (using their APIs) for school. I have 3 tabs for each categorie (Top Stories, Most Popular, Arts)
Each tab is a fragment relied to MainActivity and we can switch between fragments using a ViewPager.
Each Fragment contains a RecyclerView. (for article List)
I'm using Retrofit and Glide.
Here is my github repo if you want :https://github.com/matteovaccari/MyNews
I've successfuly implemented Article API but i've been meeting trouble using Glide here's my problem:
Not all images are display (almost 8 images / 10 are display)
and in MostPopular Tab, it's 0/10 images
I think my problem is from my method SetImage:
public void setArticleImage(Result article, RequestManager glide) {
//If article url isn't null
if (article.getMultimedia() != null) {
if (article.getMultimedia().size() > 0) {
// get image string
String urlMultimedia = article.getMultimedia().get(0).getUrl();
// clean the URL
if (urlMultimedia.startsWith("images")) {
urlMultimedia = "https://www.nytimes.com/" + urlMultimedia;
}
glide.load(urlMultimedia).apply(new RequestOptions().fallback(R.drawable.ic_launcher_background)).into(imageView);
} else {
// default image
getImageDefault(glide);
}
} else {
if (article.getMedia() == null) {
//image default
getImageDefault(glide);
} else {
// get Url
String mUrlMedia = article.getMedia().get(0).getMediaMetadata().get(0).getUrl();
// glide the string
glide.load(mUrlMedia).apply(new RequestOptions().fallback(R.drawable.ic_launcher_background)).into(imageView);
}
}
}
This method is called in my updateUI method (with setTitle, setSection, etc)
I don't understand how some images can be loaded and some don't...
Same for MostPopular tab who don't load anything
The issue is with your API response. Some of your images are not loading because your condition if (article.getMultimedia() != null) is failing in some cases. In those cases, multimedia doesn't exist at all in the API response and hence it doesn't have any imageUrl and hence your default placeholder image is loading.
public void setArticleImage(Result article, RequestManager glide) {
//This condition is FAILING for you because article doesn't have multimedia object inside it.
if (article.getMultimedia() != null) {
if (article.getMultimedia().size() > 0) {
// get image string
String urlMultimedia = article.getMultimedia().get(0).getUrl();
// clean the URL
if (urlMultimedia.startsWith("images")) {
urlMultimedia = "https://www.nytimes.com/" + urlMultimedia;
}
glide.load(urlMultimedia).apply(new RequestOptions().fallback(R.drawable.ic_launcher_background)).into(imageView);
} else {
// default image
getImageDefault(glide);
}
} else {
if (article.getMedia() == null) {
//image default
getImageDefault(glide);
} else {
// get Url
String mUrlMedia = article.getMedia().get(0).getMediaMetadata().get(0).getUrl();
// glide the string
glide.load(mUrlMedia).apply(new RequestOptions().fallback(R.drawable.ic_launcher_background)).into(imageView);
}
}
}
You should ask the developer to fix the API response.

How to download a Google Play Games Services Saved Games Snapshot's Data, silently?

I'm trying to use Google's Saved Games feature with Google Play Games Services in my Android app. Google provides sample code how to do so:
private static final int RC_SAVED_GAMES = 9009;
private String mCurrentSaveName = "snapshotTemp";
private void showSavedGamesUI() {
SnapshotsClient snapshotsClient =
Games.getSnapshotsClient(this, GoogleSignIn.getLastSignedInAccount(this));
int maxNumberOfSavedGamesToShow = 5;
Task<Intent> intentTask = snapshotsClient.getSelectSnapshotIntent(
"See My Saves", true, true, maxNumberOfSavedGamesToShow);
intentTask.addOnSuccessListener(new OnSuccessListener<Intent>() {
#Override
public void onSuccess(Intent intent) {
startActivityForResult(intent, RC_SAVED_GAMES);
}
});
}
#Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
if (intent != null) {
if (intent.hasExtra(SnapshotsClient.EXTRA_SNAPSHOT_METADATA)) {
// Load a snapshot.
SnapshotMetadata snapshotMetadata =
intent.getParcelableExtra(SnapshotsClient.EXTRA_SNAPSHOT_METADATA);
mCurrentSaveName = snapshotMetadata.getUniqueName();
// Load the game data from the Snapshot
// ...
} else if (intent.hasExtra(SnapshotsClient.EXTRA_SNAPSHOT_NEW)) {
// Create a new snapshot named with a unique string
String unique = new BigInteger(281, new Random()).toString(13);
mCurrentSaveName = "snapshotTemp-" + unique;
// Create the new snapshot
// ...
}
}
}
Obviously, Google wants you to use their provided intent to let the user decide which saved game to load or if a new save game should be created.
I, on the other hand, want to do this decision for the user. However, I'm unable to find a way to return a list of snapshots and to load snapshot data.
Since my game won't require to maintain more than one saved game per user I'm less interested in getting a list of snapshots without an intent (which would be an interesting solution, though) and more in loading a snapshot based on the name of the saved game, silently.
How can I load a snapshot without showing an intent?
The comment of jess leaded me to a solution that is now deprecated. However the person who posted the answer pointed out that there is also a working solution in the CollectAllTheStars sample app that is provided by Google. I was tempted to check this sample app to find out if the Google team has changed the code to fit the new way. For my amuse the comments in that sample app were describing the old deprecated way, still, but the code was changed for my luck.
Inspecting the code gave me ideas, so I came up with this solution:
String serializedSavedGameData;
public void downloadSavedGameData(final String name) {
if(snapshotsClient != null) {
snapshotsClient.open(name, true, SnapshotsClient.RESOLUTION_POLICY_MOST_RECENTLY_MODIFIED).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.e(TAG, "Error while opening snapshot: ", e);
}
}).continueWith(new Continuation<SnapshotsClient.DataOrConflict<Snapshot>, byte[]>() {
#Override
public byte[] then(#NonNull Task<SnapshotsClient.DataOrConflict<Snapshot>> task) throws Exception {
Snapshot snapshot = task.getResult().getData();
// Opening the snapshot was a success and any conflicts have been resolved.
try {
// Extract the raw data from the snapshot.
return snapshot.getSnapshotContents().readFully();
} catch (IOException e) {
Log.e(TAG, "Error while reading snapshot: ", e);
} catch (NullPointerException e) {
Log.e(TAG, "Error while reading snapshot: ", e);
}
return null;
}
}).addOnCompleteListener(new OnCompleteListener<byte[]>() {
#Override
public void onComplete(#NonNull Task<byte[]> task) {
if(task.isSuccessful()) {
byte[] data = task.getResult();
try {
serializedSavedGameData = new String(data, "UTF-16BE");
} catch (UnsupportedEncodingException e) {
Log.d(TAG, "Failed to deserialize save game data: " + e.getMessage());
}
} else {
Exception ex = task.getException();
Log.d(TAG, "Failed to load saved game data: " + (ex != null ? ex.getMessage() : "UNKNOWN"));
}
}
});
}
}
I implemented a simple resolving policy (take the newest saved game on conflicts), but I had no time to hard test all the different cases like conflicts, but it passed my simple tests so far.
Hopefully anybody can profit from this.

Share non-parcelable item between App and service

I'm looking for one way to share a non-parcelable item from my application and my current Service. This is the situation:
I have a Service to store all the media data from a camera application, photos, videos etc. The mission of this service is continue saving the media when the user go to background. When I did this in a first instance, I had a lot of SIGSEGV errors:
08-22 10:15:49.377 15784-15818/com.bq.camerabq A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x8c3f4000 in tid 15818 (CameraModuleBac)
This was because the Image item that I recover from my imageReaders are not parceable, I fix this saving the Bytebuffers from the image instead of the whole image item.
But, now I'm getting the same problem with DNG captures, because from my imageReader I got a CaptureResult item that I need to use to create a DngCreator item to write the dng image.
CaptureResults and DngCreators are not parcelables or Serializables, so I don't find a way to save my data from the application to recover it in the service if I'm in background.
I have tried to copy the reference when calling the Service and it didn't worked. Also I saw in other posts as Object Sharing Between Activities in Android and Object Sharing Between Activities in Android that I can save the item in a static reference in my application context to be able to recover it in different activities. So finally I tried this:
public class DngManager extends Application {
public static DngManager sDngManagerInstance;
protected Hashtable<String, CaptureResult> dngCaptureResults;
private static String DNG_KEY_PREFIX = "dngResult_";
public DngManager(){
super();
createDNGCaptureResults();
}
public void createDNGCaptureResults() {
dngCaptureResults = new Hashtable<String, CaptureResult>();
}
public boolean addDNGCaptureResultToSharedMem(long dateKey, CaptureResult value) {
dngCaptureResults.put(DNG_KEY_PREFIX + dateKey, value);
return true;
}
public CaptureResult getFromDNGCaptureResults(long dateKey) {
return dngCaptureResults.get(DNG_KEY_PREFIX + dateKey);
}
private boolean containsDNGCaptureResults(long dateKey) {
return dngCaptureResults.containsKey(DNG_KEY_PREFIX + dateKey);
}
public void clearDNGCaptureResults(long dateKey) {
String partKey = String.valueOf(dateKey);
Enumeration<String> e2 = dngCaptureResults.keys();
while (e2.hasMoreElements()) {
String i = (String) e2.nextElement();
if (i.contains(partKey))
dngCaptureResults.remove(i);
}
}
public static DngManager getInstance(){
if (sDngManagerInstance == null){
sDngManagerInstance = new DngManager();
}
return sDngManagerInstance;
}
}
And later I recover it in my service:
CaptureResult dngResult = ((DngManager)getApplication()).getFromDNGCaptureResults(mDngPicture.getDateTaken());
if (dngResult == null) {
return;
}
DngCreator dngCreator = new DngCreator(mCameraCharacteristics, dngResult);
path = Storage.generateFilepath(title, "dng");
file = new File(Uri.decode(path));
try {
Log.e(TAG, "[DngSaveTask|run] WriteByteBuffer: Height " + mDngPicture.getSize().getHeight() + " Width " + mDngPicture.getSize().getWidth());
OutputStream os = new FileOutputStream(file);
dngCreator.writeByteBuffer(os, mDngPicture.getSize(), mDngPicture.getDngByteBuffer(), 0);
} catch (IOException e) {
Log.d(TAG, "[DngSaveTask|run] " + e);
e.printStackTrace();
}
dngCreator.close();
Log.e(TAG, "[DngSaveTask|run] Cleaning Result from shared memory");
DngManager.getInstance().clearDNGCaptureResults(mDngPicture.getDateTaken());
MediaScannerConnection.scanFile(getApplicationContext(), new String[]{file.getAbsolutePath()}, null, null);
Anyway, it still giving me back a SIGSEGV error. What else can I try?

Android ArrayList size set to zero after call

I'm currently working on a basic application that makes a query to parse (back4app) backend, the aim is to retrive ParseGeoPoint from every row in the class "Annuncio" and display them in the google map.
The query works fine, I have an ArrayList that is populated after the query:
ParseQuery<ParseObject> query = ParseQuery.getQuery(Keys.CLASS_NAME);
query.findInBackground(new FindCallback<ParseObject>() {
public void done(List<ParseObject> objects, ParseException e) {
if (e == null) {
for(ParseObject object : objects){
createAnnuncio(object);
}
progressDialog.hide();
} else {
Log.d("score", "Error: " + e.getMessage());
}
}
});
the createAnnuncio method is this:
private void createAnnuncio(ParseObject object) {
Azienda azienda = new Azienda();
azienda.setWebSite(object.getString(Keys.SITO_WEB));
azienda.setEmail(object.getString(Keys.EMAIL));
azienda.setPhoneNumber(object.getString(Keys.TELEFONO));
azienda.setLocation(object.getParseGeoPoint(Keys.LOCATION));
azienda.setNomeAzienda(object.getString(Keys.NOME_AZIENDA));
azienda.setDescAzienda(object.getString(Keys.DESC_AZIENDA));
Annuncio annuncio = new Annuncio();
annuncio.setAzienda(azienda);
annuncio.setCompleteText(object.getString(Keys.TESTO_ANNUNCIO));
annuncio.setJobKind(object.getString(Keys.TIPO_LAVORO));
annuncio.setSettore(object.getString(Keys.SETTORE));
annuncio.setTitolo(object.getString(Keys.TITOLO_ANNUNCIO));
annuncio.setFineAnnuncio(object.getDate(Keys.FINE_ANNUNCIO));
mAnnunci.add(annuncio);
}
Basically mAnnunci is a global variable:
public List<Annuncio> mAnnunci = new ArrayList<>();
Even the debug works fine, i have just 2 rows and the mAnnunci.size() is 2 after the for cycle in the query, but then the debugger takes me to some native code of parse sdk and i don't know why but after the query mAnnunci is empty.
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
showSpinner();
getAllPostsFromParse(); // retrive all posts from parse backend
//inside getAllPostsFromParse the size is 2
setUpMarkers(); // here the size is 0 (the debug print is the first instruction, so I don't modify it)
}
And most of all sorry for my bad english!

Query returning multiple times the same result - Parse4J

As a preface, I'll apologize : I'm french, so there are French words in my queries and my tables.
Anyways, I've been having problem with the queries frome Parse4J (which is using the base model of the Android API from Parse.com).
In my data on Parse.com, I have Themes and SubThemes in the same table, but Themes have as RootTheme "Root", while SubThemes have as RootTheme another Theme.
I've been trying to add the results of a query into a ComboBox, but the Query has been returning me 5 times the same result, instead of the 5 RootThemes.
Here's my code :
private ParseQuery<ParseObject> themeQuery;
private ObservableList<String> themeComboData;
#Override
public void initialize(URL location, ResourceBundle resources) {
themeComboData = FXCollections.observableArrayList();
themeQuery = ParseQuery.getQuery("Theme").whereEqualTo("RootThemeID", "DBWw03ygSv");
themeQuery.findInBackground(new FindCallback<ParseObject>() {
#Override
public void done(List<ParseObject> themeList, ParseException e) {
if (e == null) {
for(int i = 0; i < themeList.size(); i++){
ParseObject themeTemp;
themeTemp = new ParseObject("Theme");
themeTemp = themeList.get(i);
themeComboData.add(themeTemp.getString("Name"));
}
} else {
Logger.getLogger(AmIApp.class.getName()).log(Level.SEVERE, null, e);
}
}
});
themeCombo.setItems(themeComboData);
}
But all it does is filling my Combo Box themeCombo with 5 times "Affinités", instead of the 5 different RootThemes.
Any advice ? Thanks in advance :)
Edit : Here's a crop of the Table I'm using :
http://imgur.com/USpinhE
Edit :
here is the query done with the ios
PFQuery * themeQuery = [PFQuery queryWithClassName:#"Theme"];
[themeQuery whereKey:#"RootThemeID" equalTo:#"DBWw03ygSv"];
[themeQuery findObjectsInBackgroundWithBlock:^(NSArray* themes, NSError* error) {
if (!error) {
for (PFObject *th in themes ) {
NSLog(#"theme is : %#", th[#"Name"]);
}
}else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
It returns 5 different themes as expected, I’d like to do the same with Parse4J in java.

Categories