Firebase storage 'getBytes' doesn't have a cancel method - android

I am downloading a json file from storage using 'getBytes', but I want to cancel the download if it took more than 10 seconds. the method 'getBytes' returns a a 'Task' type which doesn't have a cancel method, unlike 'getFile' which returns 'FileDownloadTask' that indeed has a cancel method.
So, Is there a way to cancel the download using 'getBytes', Can I cast 'Task' to 'FileDownloadTask' ?
Edit: Here is my code :
mainStorageRef.child(UNIVERSITIES_DATA_STORAGE_PATH).getBytes(Long.MAX_VALUE).addOnSuccessListener(new OnSuccessListener<byte[]>() {
#Override
public void onSuccess(byte[] bytes) {
String jsonData = new String(bytes, StandardCharsets.UTF_8);
...
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
FastUtils.toastAndLogConnectionFailure(context, e);
}
});

firebaser here
As far as I can see from the (closed source) implementation, the task that you get back from getBytes() is not a FileDownloadTask. It is instead a direct Task<byte[]> that is constructed by a TaskCompletionSource object. For that reason, it can't be cancelled.
Your request seems reasonable though, so I'd recommend filing a feature request to make the engineering team aware of it.
For the moment, I'd recommend using the getStream() method, which gives you a StreamDownloadTask task that can be cancelled. You can then read the stream contents into a ByteArrayOutputStream, which is pretty much what the getBytes method does internally.

Related

Subscribe to observable after dispose

i am building my app on android repository by Fernando Cejas and i have a problem with subscribing to observable after calling dispose.
When i come to dashboard, i call method subscribeOnUserMessages.execute(new Subscriber(), new Params(token)), which is method in UseCase class
public void execute(DisposableObserver<T> observer, Params params) {
Preconditions.checkNotNull(observer);
final Observable<T> observable = this.buildUseCaseObservable(params)
.subscribeOn(Schedulers.from(threadExecutor))
.observeOn(postExecutionThread.getScheduler());
addDisposable(observable.subscribeWith(observer));
}
In child class SubscribeOnUserMessages i simply call repository like this
return messageRepository.subscribeOnUserMessages(params);
In my socket implementation i create like this
return Observable.create(emitter -> {
if (!isThereInternetConnection()) {
Timber.w("Network connection exception");
emitter.onError(new NetworkConnectionException());
return;
}
/*
* Open socket if not opened
*/
openSocket(params.getToken());
String channelName = CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid();
if (subscribedChannels.contains(channelName)) {
Timber.d("Channel %s is already subscribed", channelName);
return;
}
JSONObject auth;
try {
auth = createAuthJson(CHANNEL, channelName, params.getToken());
} catch (JSONException e) {
Timber.e("Couldn't create auth json");
emitter.onError(e);
return;
}
mSocket.emit(SUBSCRIBE, auth);
Timber.d("Emitted subscribe with channel: %s ", CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid());
subscribedChannels.add(CHANNEL_PRIVATE_USER + params.getAuthenticated().getUuid());
Timber.d("Subscribing on event: %s\n with user: %s", EVENT_USER_NEW_MESSAGE, params.getAuthenticated().getUuid());
if (mSocket.hasListeners(EVENT_USER_NEW_MESSAGE)) {
Timber.v("Socket already has listener on event: %s", EVENT_USER_NEW_MESSAGE);
return;
}
mSocket.on(EVENT_USER_NEW_MESSAGE, args -> {
if (args[1] == null) {
emitter.onError(new EmptyResponseException());
}
Timber.d("Event - %s %s", EVENT_USER_NEW_MESSAGE, args[1].toString());
try {
MessageEntity messageEntity = messageEntityJsonMapper.transform(args[1]);
emitter.onNext(messageEntity);
} catch (JSONException e) {
Timber.e(e, "Could not parse message json");
emitter.onError(e);
}
});
});
Symptoms are that first time i subscribe everything is going through to presentation layer. When i dispose after going to second screen and come back i only see logs coming to socket implementation, but not going through.
My question is: Is there a method for subscribing to same observable again? I've already tried to save that observable in my use case in singleton and subscribe to that observable, didn't help.
Without additional info and details regrading socket implementation it is hard to spot the problem exactly, but, from the code you've posted, you don't have dispose logic, so while you might properly call dispose() to the Observable at the correct lifecycle event, your socket will actually stay open, and it might not got disconnected/closed properly ever.
That might lead to a problems opening and connecting to the socket at the 2nd time, as you might try to reopen already open socket and depends on your internal socket impl that might be a problem.
(I can see in the comment that openSocket if not already opened, but still there might be problem elsewhere calling some method on the socket multiple times or setting listeners, again depends on the socket impl)
As a general guidelines, you should add dispose logic using emitter.setCancellable()/emitter.setDisposable() in order to dispose properly the socket resources when you no longer need them, thus - when applying subscribe again (whether the same object or not) will invoke your subscription logic again that will reopen the socket and listen to it.
It is not clear to me if you like to keep the socket open when you moving to a different screen (I don't think it is a good practice, as you will keep this resource open and might never get back to the screen again to use it), but if that's the case as #Phoenix Wang mentioned, you can use publish kind operators to multicast the Observable, so every new Subscriber will not try to reopen the socket (i.e. invoking the subscription logic) but will just get notify about messages running in the already opened socket.

Get one result from 2 observables with RxJava

I am new at RxJava and I have some pain to execute my first 'difficult' query.
I have two Observables generated from Retrofit, one that 'ping' a new api, the other the old one. The first one will query 'http://myurl.com/newapi/ping', the second one 'http://myurl.com/oldapi/ping'. Result from this request doesn't matter, I just want to know if the server is using the new or old api.
So I would like to call both observables at the same time, and finally have a boolean at the end to know if I'm using old or new api.
I tried something like that
Observable.mergeDelayError(obsOldApi,obsNewApi)
.observeOn(AndroidSchedulers.mainThread(), true)
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String s) {
}
});
But onError will be called once (I would like it to be called only if both request failed) and when onNext is called, I don't know from which request it came (old or new api ?).
Thank you for you help
For simplicity, let say that you'll received "NEW" or "OLD" regarding which api is available.
The difficulty of your operation is to manage errors : RxJava deals errors as terminal state. So you'll have to ignore this error, using .onErrorResumeNext() for example.
Observable<String> theOld = oldApi.map(r -> "OLD")
// ignore errors
.onErrorResumeNext(Obervable.empty());
Observable<String> theNew = newApi.map(r -> "NEW")
.onErrorResumeNext(Obervable.empty());
Observable.merge(theOld, theNew)
.first() // if both api are in errors
.subscribe(api -> System.out.println("Available API : "+api));
I added the operator first : it will take only the first result ("OLD" or "NEW") but trigger an error if the previous Observable is empty, which is the case if both API are unavaible.

How to resume a file download in firebase Storage?

I need to download a big file using firebase storage,
it is possible my user face network interruption through the download and i would like to be able to resume this download task.
FileDownloadTask downloadTask = firebaseStorage.getReferenceFromUrl(url).getFile(localFile);
downloadTask.addOnProgressListener(new OnProgressListener<FileDownloadTask.TaskSnapshot>() {
#Override
public void onProgress(FileDownloadTask.TaskSnapshot taskSnapshot) {
Log.v(TAG, "progress" + taskSnapshot.getBytesTransferred());;
}
});
downloadTask.addOnFailureListener(e -> {
CustomLogger.v(TAG, "downloadTask, on Failed: " + courseId);
deleteFilesAfterError(courseId);
});
downloadTask.addOnPausedListener(taskSnapshot
-> CustomLogger.v(TAG, "downloadTask, on paused: " + courseId));
the two last listener are never called. when i set up the complete listener:
downloadTask.addOnCompleteListener()
my app crashes.
even when i set up :
firebaseStorage.setMaxDownloadRetryTimeMillis(2000);
then switch off my network in the middle of a download, the downloadTask fail listener is not trigger. The task still seems to be "in progress" but there no data are downloaded. The documentation is poor on this feature. how am i supposed to implement it?
the fail listener seems broken
when i add a completeListener it crashes my app
firebaseStorage.setMaxDownloadRetryTimeMillis does not seems to have
any effect
any idea on how to tackle a resume feature for a file download?
When you say you switch off your network in the middle of the download, how are you doing this?
The reason I ask is because if you are leaving the activity temporarily to turn off the network stream then your problem might be an activity lifecycle problem.
If you leave the activity and do not handle this in the onPause(), onStop() and so on then you might experience this sort of behavior.
Other than that, your code looks similar to the docs (and my code that works fine), other than the fact that you didn't 'chain' your methods to add the listeners (shouldn't be a problem).
However, your code that you copied and pasted here is partially folded and does not show whether or not you are overiding the onFailure(Exception e) method, but I assume it is folded up in that arrow.
If you are still working on this, can you post the trace for any errors you're receiving on the crash?
Here is a block of code that works during network failure, just for a reference:
FirebaseStorage storage = FirebaseStorage.getInstance();
StorageReference storageReference = storage.getReferenceFromUrl("gs://your-bucket/");
storageReference.getFile(localFile).addOnSuccessListener(YourActivity.this, new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
#Override
public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
}
}
}).addOnFailureListener(YourActivity.this, new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception exception) {
}
}

Particular overload of Azure Mobile Service invokeApi is not working while calling custom API

Where is the documentation/sample for all overloads of invokeApi function for Azure Mobile Service client SDK for Android?
I found this article and tried following code, which does not work. There are no compile time or run time errors, invokeApi gets called, but it does not come back to onSuccess or onFailure. If I call invokeApi without order object, everything works as expected
PizzaOrder order = new PizzaOrder();
order.Size = "Large";
order.Flavor = "Four cheeses";
order.UserPhone = "555-555-1234";
ListenableFuture<PizzaOrderResponse> testresult = mClient.invokeApi("bookservice", order, PizzaOrderResponse.class);
Futures.addCallback(testresult, new FutureCallback<PizzaOrderResponse>() {
#Override
public void onFailure(Throwable exc) {
// failure handling code here
}
#Override
public void onSuccess(PizzaOrderResponse testresult) {
// success handling code here
}
});
One of the properties in the data object being returned by the custom API had incorrect data type. I am still not sure where the good documentation is and why custom API call did not fail but at least it is working now.

Picasso errors on callback

I've noticed that occasionally images won't load in my app through picasso and that picasso is in fact erring. I am using two images per list item in a list view. Here's the picasso code:
Picasso.with(DashboardActivity.this).load(status).into(iv_customer_status_pic, new Callback() {
#Override public void onSuccess() {
Log.d("Debug", "Picasso Success");
}
#Override public void onError() {
Log.d("Debug", "Picasso Errored");
}
});
How can I ensure that the images are loaded, I don't want them to error and then make them disappear. Also why does it error? Is there a timeout? I noticed on more powerful devices it happens less.
The reasons why it fails might because of no Internet connection and Invalid Image URL.
With regard to the error handling refer to nPn's answer.
The reason the onError() callback for Picasso.with().load().into(target, callback) exists is because there is no 100% guarantee the load will be successful. For example if you are trying to load from a uri and you don't have an internet connection, the load will not be successful.
You can somehow attempt a re-try (which I think is already built into Picasso), but ultimately, you need to handle the case were the load fails (for whatever reason). One option would be to load a "default" image, like a generic "profile picture" if you were trying to load a specific users profile picture.
If you move the implementation of the callbacks to a separate class , or even the containing class you should be able to retry from the onError() call back. Here is what I am thinking:
class ContainingClass implements Callback.EmptyCallback
private int mRetryAttempts = 0;
#Override
public void onError() {
if (mRetryAttempts < 2) {
mRetryAttempts++;
// try again
} else {
mRetryAttempts = 0;
}
}
#Override
public void onSuccess() {
mRetryAttempts = 0;
}

Categories

Resources