I'm using parse.com Android SDK to manage some images in my app. Is cancel() the only way to stop a transaction with the parse.com server?
Minimal example:
final ParseFile file = ... ;
file.getDataInBackground(new GetDataCallback() {
//called when loading is done
#Override
public void done(byte[] bytes, ParseException e) {
Log.e(TAG, String.valueOf(bytes == null));
}
}, new ProgressCallback() {
//called to notify progress
#Override
public void done(Integer integer) {
Log.e(TAG, String.valueOf(integer));
if (integer > 50) {
file.cancel();
}
}
});
I would expect that loading stops after the 50% is reached, but it does not. My log in this situation would be:
1
2
3
....
49
50
51
....
98
99
100
true
The only difference from times you call cancel() and times you don't, is that if you canceled the byte[] result is null. But that is really secondary, the point here is that file keeps consuming bandwidth and, moreover, overlaps with future downloads, slowing things down.
Is there any way to really stop ParseFile loading? Do you see any workaround, like stopping its thread? Maybe something using the underlying bolts framework? Using my own async task?
Example: continueWhile() method might be useful, but I can't figure out how to use it.
I would like to know the reason for the downvote, maybe the common title? That is really what I am experiencing: ParseFile.cancel() is not working. And it should, according to the official docs.
Comments suggest that I should simply call break. While I don't think it would work, I might clarify that the code I posted was just a minimal, concise working example providing both context and the issue. I don't want to cancel() the transaction from inside the progress callback; I want to call parseFile.cancel() from everywhere. I put in the progress callback to show that, while it should stop, it doesn't.
Edit This is what I'm really trying to do. I have tried different ways but this is it.
ParseFile currentFile;
public void setFile(ParseFile file) {
if (currentFile != null) {
currentFile.cancel();
}
currentFile = file;
currentFile.getDataInBackground(new GetDataCallback() {
...
}, new ProgressCallback() {
... // logs
});
}
With such code and say, two big images to download, things go like:
//calling setFile(file1)
1
2
3
...
20
21
22
//calling setFile(file2), thus also calling file1.cancel()
1
2
23 //file1 going on!
3
24
4
25
... //things get slower and this screws up progress bars and such.
TL;DR;
The only conclusion I can draw at this point is that there is a difference in our implementations that is causing cancel to fail for you.
EDIT: this seems to be the case as seen in your own answer. The difference being SDK versions. https://stackoverflow.com/a/32034500/2680506
Full Answer:
The description for the cancel() method:
"Cancels the current network request and callbacks whether it's uploading or fetching data from the server."
I was curious about this so I did a little testing of my own. I took my app, made a ParseFile from the bytes of an image and attempted to save it in the background.
Test 1
Bitmap file = BitmapFactory.decodeResource(context.getResources(), R.drawable.background);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
file.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
final ParseFile myTestFile = new ParseFile(byteArray);
myTestFile.saveInBackground(new SaveCallback(){
#Override
public void done(ParseException e) {
if(e == null)
{
Log.i(null, "Done saving.");
}
}
}, new ProgressCallback(){
#Override
public void done(Integer progress) {
Log.i(null, "Progress at " + progress + "%");
if(progress > 50)
{
myTestFile.cancel();
}
}});
//myTestFile.cancel();
Test 2
Bitmap file = BitmapFactory.decodeResource(context.getResources(), R.drawable.background);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
file.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
ParseFile myTestFile = new ParseFile(byteArray);
myTestFile.saveInBackground(new SaveCallback(){
#Override
public void done(ParseException e) {
if(e == null)
{
Log.i(null, "Done saving.");
}
}
}, new ProgressCallback(){
#Override
public void done(Integer progress) {
Log.i(null, "Progress at " + progress + "%");
}});
myTestFile.cancel();
The results for Test 1 were similar to what you describe, because the file is very small I only got one progress callback at 100% but then it also invoked the SaveCallback.
In Test 2, however, the cancel() method appears to function as one would expect, resulting in no logs or callbacks.
It appears that cancel fails to work because you are calling it from the Callback. This is consistent with the fact that you continue to see ProgressCallbacks after you originally cancel in your own tests.
EDIT
I just uploaded an image and tested cancel for myself, in the onCreate() method of my activity I have this code:
ParseQuery<ParseObject> newQuery = ParseQuery.getQuery("TestObject");
newQuery.findInBackground(new FindCallback<ParseObject>(){
#Override
public void done(List<ParseObject> objects, ParseException e)
{
ParseFile myTestFile = objects.get(0).getParseFile("file");
myTestFile.getDataInBackground(new GetDataCallback()
{
#Override
public void done(byte[] data, ParseException e)
{
Log.i(null, "Download finished");
}
},
new ProgressCallback()
{
#Override
public void done(Integer percentDone)
{
Log.i(null, "Download at " + percentDone + "%");
}
});
//myTestFile.cancel();
}});
When cancel is commented, It will enter the GetDataCallback with a populated byte array. When cancel is not commented no call back will occur. Strangely enough the ProgressCallback is never called although it says it is guaranteed. However, it still appears that cancel is working for me.
Looks like this was a real bug, now fixed. I used to stay with the v1.9.2 release; today I updated to v1.10.0 and everything works fine.
Related
I'm working on an existing Android App with parse back-end (localDatastore is enabled but not used in this context) which has the following object structure:
Object A has an array of objects B
Object B has an array of objects C
I save this object structure using saveInBackground in calling the next saveInBackground in the done SaveCallback in reverse Order(C,B,A). For the inner two that works fine, but the top level object isn't saved.
Here's the code (frame, newStep and order are objects of classes inheriting from the ParseObject class)
frame.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Log.i("Info", "frame.save callback OK");
frames.add(frame);
newStep.setTimeFrames(frames);
newStep.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Log.i("Info", "newStep.save callback OK");
List<ProcessingStep> steps = order.getProcessingSteps();
steps.add(newStep);
order.setProcessingSteps(steps);
order.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null){
Log.i("Info", "order.save callback OK");
}else{
Log.i("Info", "order.save callback FAIL");
}
}});
} else {
Log.i("Info", "newStep.save callback FAIL");
e.printStackTrace();
}
}
});
} else {
Log.i("Info", "frame.save callback FAIL");
e.printStackTrace();
}
}
});
In the console log I see only "frame.save callback OK", the "newStep.saveInBackground()" seems to be executed too (object appears in backend) however I never get the log message in the callback.
If I save all objects before synchronously without references to each other first and then call the code here, it seems to work (worked at least once) but took for ever (minutes). Queries from the back-end are super fast and the frame object is also saved almost instantly but the done-callbacks seem to bugging. When it fails I do not get any exception, log anything it just seems to fail silently.
I'm looking for any insight why Parse behaves like that as well as how to fix it.
edit: The problem seems to be with the double relation (A to B and B to C). If I try with only A to B or B to C it works just fine. What remains mysterious to me, however, is why splitting the operation up with callbacks doesn't seem to work.
The problem was the enabled localdatastore. Without localdatastore enabled everything works as it should.
I am trying to download images if they don't exist yet, and keep and re-use them if they've already been downloaded.
While downloading and displaying works, re-using them does not. It seems like mValues isn't the same mValues in different calls.
Here is my code
public void onBindViewHolder(final TabFragment1.SimpleItemRecyclerViewAdapter.ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
holder.mIdView.setText(mValues.get(position).title);
holder.mContentView.setText(mValues.get(position).description);
if (holder.mThumbnail == null) {
System.out.println("thumbnail null"); //NEVER gets called
} else {
if(mValues.get(position).thumbnailData!=null){
System.out.println("thumbnailData contained"); //NEVER GETS CALLED, IT ALWAYS SEEMS TO BE NUL
holder.mThumbnail.setImageDrawable(mValues.get(position).thumbnailData);
} else {
System.out.println("thumbnailData downloading"); //always happening
try {
AsyncTask<String, Void, Bitmap> execute = new DownloadImageTask((ImageView) holder.mThumbnail)
.execute(holder.mItem.thumbnailUrl);
mValues.get(position).thumbnailData = holder.mThumbnail.getDrawable();
if(mValues.get(position).thumbnailData!=null) {
System.out.println("is null"); //hardly ever gets printed
} else {
System.out.println("is not null"); //always gets printed
}
} catch (Exception ex) {
}
}
}
}
SOLVED: my code was wrong. The thumbnail update didn't wait for the AsyncTask to finish and I messed up the if statement so it tricked me into thinking the "new" thumbnail worked. I put the thumbnail update into the AsyncTask and now it's working :)
Your if else statement, inside try catch has interchanged log statements...Here your if statement checks for (thumbnail !=null) but prints it as null if found true.... so my point here is, your thumbnail object is always null, as its value would depend upon completion of Asynctask you started just above it, hence you cannot access it just like that, because this downloading process will run on separate thread.
You could use Picasso library for android, that will cache images and other cool things for you.
You can use any good Image loading library like UIL or Picasso
or Glide.
You can also try setDrawingCacheEnabled(true) on your recycler view.
Trying to download publicly accessible file from kinvey to android app
tried following code,
FileOutputStream fos = new FileOutputStream(videoName);
FileMetaData meta = new FileMetaData(videoName);
meta.setId(videoName);
kinveyClient.file().downloadWithTTL(meta.getId(), 1200000, fos, new DownloaderProgressListener() {
#Override
public void progressChanged(MediaHttpDownloader downloader) throws IOException {
Log.i("", "progress updated: " + downloader.getDownloadState());
final String state = new String(downloader.getDownloadState().toString());
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
//tProgress.setText((tProgress.getText() == null) ? state : tProgress.getText() + "\n" + state)
}
});
}
#Override
public void onSuccess(Void result) {
}
#Override
public void onFailure(Throwable error) {
}
});
getting event success but data is not being received. any idea how this will work??
getting error like this :
failure com.kinvey.java.core.KinveyJsonResponseException: InsufficientCredentials
The credentials used to authenticate this request are not authorized to run this operation. Please retry your request with appropriate credentials
According to the documentation, this is how you are supposed to use the value you get from getDownloadState() . In order to get the progress of your download, you need to first check the downloadState is equal to DOWNLOAD_IN_PROGRESS value, and then use the download.getProgress() method to get the progress, then you can use this to update the value on your progress slider, (you shouldn't be converting these values to string using the toString() method like you've done above.
public void progressChanged(MediaHttpDownloader downloader) throws IOException {
switch (downloader.getDownloadState()) {
case DOWNLOAD_IN_PROGRESS:
//Download in progress
//Download percentage: + downloader.getProgress()
break;
case DOWNLOAD_COMPLETE:
//Download Completed!
break;
}
}
Code taken from documentation here
Nayana,
You might be getting this error because user does not have permission to access that file. You can try following to solve your issue:
If you make the file publicly readable, all authenticated users will be able to access that particular file.
You can make the file accessible by adding read/write permissions for
few users in ACL.
Also check the documentation for Access Control List:
http://devcenter.kinvey.com/rest/guides/security#entityanduserpermissions
Thanks,
Pranav
Kinvey Support
In the setup part of my app, I want to take a picture then display it. I want to display a Progress Dialog while saving the image to disk. My ProgressDialog code works except on a Droid Mini running KitKat.
I have tried a number of approaches, including this blog https://www.workreloaded.com/2011/06/how-to-use-the-android-camera/. I liked the approach but the ProgressDialog did not appear for me.
I think the root of the problem is some timing issue using the UI thread. But I can't precisely diagnose it and definitely can't solve it. My questions:
1. Does camera.takePicture() run on the UI thread?
2. What would prevent a ProgressDialog from appearing?
3. Does someone have a working example (besides the above post) that I could use?
I'm posting code that works but is bad practice and code that doesn't work but would be more maintainable.
The following code with a test for KitKat works, but is bad practice.
public class PreviewTestNextButtonListener implements Button.OnClickListener {
private static final String TAG = "PreviewTestButtonListener";
PreviewTest previewTest;
Camera camera;
public PreviewTestNextButtonListener(PreviewTest context, Camera camera) {
this.previewTest = context;
this.camera = camera;
}
public void onClick(View v) {
Log.v(TAG, "Taking picture");
try {
PhotoSaverPreviewTest photoSaver = new PhotoSaverPreviewTest(previewTest);
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.KITKAT) {
// don't display the progress dialog in kit kat because my Droid Mini running KitKat has a problem with
// this. But the Mini is so fast that the save is instantaneous
previewTest.showProgressDialog();
}
// photoSaver will launch the StoredRotationTest
camera.takePicture(null, null, photoSaver);
} catch (Exception e) {
Log.e(TAG, "failed to start Stored Rotation Test because " + e, e);
}
}
}
In an alternative version, I tried simply calling the ProgressDialog from takePicture() but that doesn't work. I also tried an AsyncTask, but that did not present a ProgressDialog either.
public void onPictureTaken(final byte[] imageData, Camera camera) {
Log.e(TAG, "starting onPictureTaken");
// **** this does not present the ProgressDialog
activity.showProgressDialog();
if (imageData != null) {
Log.e(TAG, "got image data");
camera.startPreview();
Display display = ((android.view.WindowManager) activity.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
int displayRotation = display.getRotation();
try {
// showProgressDialog();
// save the image to disk in the background
RotateAndWriteImageAsync imageWrite = new RotateAndWriteImageAsync(activity, displayRotation, imageData);
imageWrite.mySpecialMove();
Intent intent;
// during set up, launch the stored image rotation test
intent = new Intent(activity, StoredRotationTest.class);
activity.startActivity(intent);
PreviewTest.dismissProgressDialog();
} catch (Exception e) {
Log.e(TAG, "error saving image or start rotation test: " + e);
}
} else {
Log.e(TAG, " ");
Log.e(TAG, "NO IMAGE DATA passed in parameter from camera");
Log.e(TAG, " ");
}
}
And finally, the method that presents the ProgressDialog from the PreviewTest Activity.
/** Called by Next Button which is a bit of kludge.*/
public void showProgressDialog() {
if (progress == null) {
progress = new ProgressDialog(this);
progress.setTitle("Saving picture");
}
progress.show();
}
/** Called by StoredRotationSetup which is a bit of a kludge.*/
public static void dismissProgressDialog() {
if (progress != null && progress.isShowing()) {
progress.dismiss();
}
}
Mark Murphy(aka Commonsware) gave me some guidance during his office hours. ProgressDialogs are bad form: https://ux.stackexchange.com/questions/12637/what-research-is-there-suggesting-modal-dialogs-are-disruptive. My ProgressBar only shows when the user is waiting, but it does not pop up.
ProgressBar solved my problem. Here is the .xml:
<ProgressBar
android:id="#+id/next_progress_bar"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_above="#+id/rotate_orientation_button"
style="#android:style/Widget.ProgressBar.Large"
android:indeterminate="true"
android:visibility="invisible" />
And the Java:
// progress bar
ProgressBar progressBar = (ProgressBar) findViewById(R.id.next_progress_bar);
progressBar.setVisibility(View.VISIBLE);
I have an interesting problem that I've never run into in programming before. I have an onClickListener that does a lot of username and password checks (makes sure the username is proper length, not taken, etc). I'm using MobDB, and I was using a conditional statement that would return a row if the username already existed. The problem is that the Listener skips the DB and goes to the final check that, if everything works, posts a new username and password to my DB. How can I make it wait for a response from the DB before skipping to the last check?
Here is the relevant code:
usernamecheck3 = true;
MobDB.getInstance().execute(APP_KEY, null, rd, null, false, new MobDBResponseListener() {
#Override public void mobDBSuccessResponse() {
usernamecheck3 = false;
Log.e("mobdbSuccess:", "success");
}
#Override public void mobDBResponse(Vector<HashMap<String, Object[]>> row) {
}
#Override public void mobDBResponse(String jsonObj) {
/*Log.e("mobdbSuccess:", "jsonObj");
Log.e("mobdbSuccess:", jsonObj);
JSONObject mainObject;
try {
mainObject = new JSONObject(jsonObj);
// need to parse the json object.
} catch (JSONException e1) {
e1.printStackTrace();
} */
}
#Override public void mobDBFileResponse(String fileName, byte[] fileData) {
//get file name with extension and file byte array
}
#Override public void mobDBErrorResponse(Integer errValue, String errMsg) {
usernamecheck3 = false;
Log.e("doesnt", "work");
}
});
if(usernamecheck3 == false){
Toast.makeText(getApplicationContext(), "Username is taken, please choose another", Toast.LENGTH_SHORT).show();
}
Basically the check always returns true, and then logcat will say mobdbSuccess: success, which should have set the Bool to false.
Thanks.
MobDBResponseListener is executing on a different thread. What happens here is that the processing is split, while a thread is doing the query, the main thread on which you added the listener, skips right ahead to the validation. Your best bet is to place the validation inside the MobDBResponseListener, on the mobDBResponse method.
Try to debug your code and calls, the Listener may be using an async task. If so, you may do anything you please from the response method, as it will be executing in the main thread again. Otherwise, you should look at solutions that handle threaded execution like Handlers