Android Volley ImageListener onResponse behaviour - android

I'm using Android Volley Library and my question should be easy to answer, if you know it ;).
I need to know how com.android.volley.toolbox.ImageLoader.ImageListener exactly behaves when
handling successful responses. The docs says
The call flow is this: 1. Upon being attached to a request,
onResponse(response, true) will be invoked to reflect any cached data
that was already available. If the data was available, response.getBitmap() will
be non-null. 2. After a network response returns, only one of the following
cases will happen: - onResponse(response, false) will be called if the
image was loaded. or - onErrorResponse will be called if there was an error
loading the image.
What I want to know is this: does that mean that I can have onResponse called twice (first with isImmediate set to false and then set to true)? Can I rely on that? I mean will it always be like this (if the image loading is succesful)?
I'm trying to do something like this
imageLoader.get(image.getUrl(), new ImageListener() {
#Override
public void onErrorResponse(VolleyError error) {
callback.call(null, error);
}
#Override
public void onResponse(ImageContainer response,
boolean isImmediate) {
if (response.getBitmap() != null) {
callback.call(response.getBitmap(), null);
}
}
});
I need callback.call() to be called when the image could be loaded succesfully and I also need response.getBitmap() to return the actual bitmap and not null.
Thanks in advance!

What I want to know is this: does that mean that I can have onResponse called twice (first with isImmediate set to false and then set to true)? Can I rely on that? I mean will it always be like this (if the image loading is succesful)?
Short answer: yes.
Unless the implementation of this class is changed (and I highly doubt that will happen), you can rely on this call order being respected.
If it helps, I gave some detail on the three possible response cases in another answer here: https://stackoverflow.com/a/32464875/3227795

Related

Firebase onDataChange skips over first fire then fires normally afterwards

I'm having an issue with 2 separate methods, essentially the same issue where the database reference is firing and retrieving all the correct paths from the relevant nodes, but skips over the first fire on onDataChange then fires as expected afterwards giving the values needed.
The general context of these 2 methods is retrieving the value at the database reference using a code/value (specified path) to get to its relevant value. This value is retrieved and used elsewhere in the program.
I've looked at many problems regarding onDataChange not firing. Solved many of those issues elsewhere in my program but somehow these 2 methods are persisting with this issue. Ive run debug multiple times and dont understand how its showing and getting the correct paths but skips the first run on onDataChange where other methods implementing the exact same principle is running perfecting.
Im only posting the first issue
in onCreate
databaseReference_AUTH_TABLE = FirebaseDatabase.getInstance().getReference(AUTH_TABLE_TAG); verified = false;
Context is im using a dialog to authenticate a code. Check if the code exists in the database. And if so have the rest of the program run what i need it to do
public void authenticateProductID(final String code){
databaseReference_AUTH_TABLE.child(code).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if(dataSnapshot.exists() && !verified){//Exists and is not verified yet
PID = dataSnapshot.getValue().toString();
verified = true;
return;
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
public void showPopupProduct_btn(View view){
final Dialog dialogProductVerification = new Dialog(this);
dialogProductVerification.setContentView(R.layout.layout_popup_product);
Button authenticate = dialogProductVerification.findViewById(R.id.btnPopupProductVerification);
authenticate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
EditText verificationCode = dialogProductVerification.findViewById(R.id.editTextPopupCode);
code = verificationCode.getText().toString();
if(noDuplicateCode(code)){
authenticateProductID(code);
if(verified){
getPackage(PID, code);
txtResult.setText(code);
}
else{
Toast.makeText(POSActivity.this, "Authentication Failed", Toast.LENGTH_SHORT).show();
}
}
}
});
dialogProductVerification.show();
}
Because onDataChange isn't fired the first time, verified is false. But 2nd button click everything is perfect.
firbase node
Basically my app will be finished when this is resolved. Any help will be much appreciated. Thank you in advance
Firebase APIs are asynchronous, which means that the onDataChange() method returns immediately after it's invoked and the callback from the Task it returns, will be called some time later. There are no guarantees about how long it will take. So it may take from a few hundred milliseconds to a few seconds before that data is available. Because that method returns immediately, the value of your verified boolean that you are trying to use, is not populated from the callback yet. So simply creating it as a global variable won't help you at all.
Basically, you're trying to use a value synchronously from an API that's asynchronous. That's not a good idea. You should handle the APIs asynchronously as intended.
A quick solve for this problem would be to move the code that queries the second node inside the first callback (inside the onDataChange() method) so-called nested queries, otherwise, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.

RxJava Only want to call cache when api call is success

I am very new to RxJava and can't seem to find figure out the solution to this use case. I have been researching on this for 2 days now and no luck.
I have 2 Singles, remote and cache, to register a user in my app
I first call remote which saves the user data on a server, and returns a custom code to indicate successfully saved. I only want to call cache after I have checked the custom code from remote and gotten a success. If custom code comes as failure, I want to return that, and not go to the cache at all
The operator, which you're looking for, is flatMap. Example:
remoteApi.login().flatMap(new Function<String, SingleSource<String>>() {
#Override public SingleSource<String> apply(String response) throws Exception {
if (response.equals("success")) {
// do what you want to do with cache
return cache.save(response);
}
return Single.just(response);
}
}).subscribe(yourObserver);
Don't forget to use subscribeOn and observeOn...

How to continue with Rest calls if the first one satisfies the condition?

I'm switching from plain Retrofit to RXJava because I have to make multiple REST calls. But I'm having trouble understanding how to do the following:
First I would like to call an endpoint that returns data validity and if new data has to be fetched I have to get the new data from 5 other endpoints. After all data is retrieved and saved I have to continue to a new Activity.
I know how to make one call, but I have no idea how to do the above. Tips or links to tutorials would be greatly appreciated.
Try the following simple approach that I use in my applications: use Events
In your build.gradle module-level file:
compile 'org.greenrobot:eventbus:3.0.0'
Then, simply create a POJO class that represents your event like this:
public class DataValidationEvent{
private boolean isDataValid;
public DataValidationEvent(boolean isValid){
this.isDataValid = isValid;
}
public boolean isDataValid(){
return isDataValid;
}
}
Then, in your HTTP Request when you have received the response stating whether the data is valid (from that endpoint), notify some class or activity to do the next operation like make the expected request. Like this:
if(responseIsValid){
EventBus.getDefault().post(new DataValidationEvent(true));
}else{
EventBus.getDefault().post(new DataValidationEvent(false));
}
//obviously, you can simplify the above code to a one-liner
//by passing the actual variable returned
Next, in the activity that you need to trigger other http requests/operations, do this:
#Override public void onStart(){
super.onStart();
EventBus.getDefault().register(this);
}
#Override public void onStop(){
super.onStop();
EventBus.getDefault().unregister(this);
}
Then finally, within the same activity that you registered for events above, you need to handle (subscribe) to the event like this:
#Subscribe
public void onValidResponse(DataValidationEvent event){
//here you can call the next api request or start activity.
}
I know this might not do everything you want to do but should guide you towards an Event-driven solution that decouples your project using the popular publisher/subscriber design pattern!
Good luck and I hope this helps!
The quick-and-dirty functional solution, assuming apiClient is a retrofit client, and cacheData saves and returns the cached data object:
Observable<Data> dataObs =
apiClient
.isDataValid()
.filter(data -> !data.isValid)
.flatMap(oldData -> Observable.zip(
apiClient.fetchA(),
apiClient.fetchB(),
apiClient.fetchC(),
apiClient.fetchD(),
apiClient.fetchE(),
(a,b,c,d,e) -> cacheData(a,b,c,d,e))
.switchIfEmpty(Observable.defer(() -> getFromCache()));
Keep in mind that this will make a call to the API each time it's subscribed to; you may want to limit the frequency of such calls.

RxJava network requests and caching

I am seeking an example of a flow I'm trying to implement with help of RxJava.
Suppose I want to show a list of data. The flow should look something like this:
Read cache. If it contains the data, show it;
Send an API request to the server:
If it returned the data, then cache it and show it.
If it returned and error and there was no cached data, then show an error.
If it returned and error and there was something cached, then do nothing.
Right now I have a method that does something similar (with lots of inspiration from Jake's u2020). The main difference is that it uses in-memory caching, which means there's no need for a separate Observable for reading from cache and it can be done synchronously.
I don't know how to combine two observables (one for reading from cache and the other for API call) and obtain the flow described above.
Any suggestions?
I think I solved my problem. The observable chain looks like so:
apiCall()
.map(data -> dataInMemory = data)
.onErrorResumeNext(t -> data == null ?
Observable.just(Data.empty()) : Observable.empty())
.startWith(readDataFromCache().map(data -> dataInMemory = data))
.subscribeOn(ioScheduler)
.observeOn(uiScheduler)
.subscribe(dataRequest);
The main point is, that if readDataFromCache() throws an error, it will call onCompleted() without calling onError(). So it should be a custom Observable which you can control.
Data.empty() is a stub for my data - the Subscriber should treat it as an error.
dataInMemory is a member in my controller which acts as in-memory cache.
EDIT: the solution doesn't work properly. The completion of one use case (see comment) is not achieved.
EDIT 2: well, the solution does work properly after some tweaking. The fix was returning different types of observables depending on the state of in-memory cache. Kind of dirty.
Here is my solution:
readDataFromCache().defaultIfEmpty(null)
.flatMap(new Func1<Data, Observable<Data>>() {
#Override
public Observable<Data> call(final Data data) {
if (data == null) {
// no cache, show the data from network or throw an error
return apiCall();
} else {
return Observable.concat(
Observable.just(data),
// something cached, show the data from network or do nothing.
apiCall().onErrorResumeNext(Observable.<Data>empty()));
}
}
});
I don't add the subscribeOn and observeOn because I'm not sure readDataFromCache() should use ioScheduler or uiScheduler.

Loaders in Android Honeycomb

I'm trying to figure out how to use Loaders in Android 3.0 but can't seem to get it to work. The docs only describe using CursorLoader but I'm using AsyncTaskLoader.
From the docs it seems that you should only need to implement AsyncTaskLoader.loadInBackground() but it never gets called after getLoaderManager().initLoader() and then creating the loader in the callback.
I can see debug messages saying Created new loader LoaderInfo{4040a828 #0 : ArticleDataLoader{4036b350}} so it seems like it is created successfully.
Is it possible that loaders are currently broken in the SDK or is there some method you need to call after creating the loader? (they haven't done that in the CursorLoader example).
EDIT: Seems like calling forceLoad() on the Loader returned from initLoader() starts the loading at least but this means you can't handle rotations correctly :(
Dianne Hackborn replied on the bug tracker and referred us to the static library implementation. CursorLoader is doing forceLoad() which is why it is working.
See my attached class for a class which handles this for you in most simple cases at the bug tracker: http://code.google.com/p/android/issues/detail?id=14944
You need to override the onStartLoading() method. Look at the example on the developer website.
/**
* Handles a request to start the Loader.
*/
#Override protected void onStartLoading() {
if (mApps != null) {
// If we currently have a result available, deliver it
// immediately.
deliverResult(mApps);
}
// Start watching for changes in the app data.
if (mPackageObserver == null) {
mPackageObserver = new PackageIntentReceiver(this);
}
// Has something interesting in the configuration changed since we
// last built the app list?
boolean configChange = mLastConfig.applyNewConfig(getContext().getResources());
if (takeContentChanged() || mApps == null || configChange) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad();
}
}
Alex;
Did you try to validate if the onLoadInBackground () gets even called?
onLoadInBackground (): Called on a worker thread to perform the actual load. Implementations should not deliver the result directly, but should return them from this method, which will eventually end up calling deliverResult(D) on the UI thread. If implementations need to process the results on the UI thread they may override deliverResult(D) and do so there.

Categories

Resources