buzoherbert
buzoherbert

Reputation: 1658

Fragment asynchronous Interface callback handling

I have a Fragment that does some communication with the internet trough some helper Class that requires an interface to handle asynchronous callbacks like this:

SomeInternetCommunicator.getResource(theResourceToGet, retrieverInterfaceInstance);

The interface looks like this:

public interface SomeInternetCommunicator {
     void onResourceRetrieveSuccess(SomeResource resource);
     void onResourceRetrieveFailed(CommunicatorException e);
}

The problem is that, sometimes the calls take too long, the user already navigated elsewhere and the Fragment that did the call to getResource is not part of the currently running Activity anymore (not showing, not on backstack). This causes many problems because this orphan Fragment is still attempting to react to the callbacks even if it is not part of the Activity anymore. For example, getContext() returns null.

Now my workaround on the Fragment looks like this:

SomeInternetCommunicator flocktrackerTrackerRetrieverInterface = new SomeInternetCommunicator() {
            @Override
            public void onResourceRetrieveSuccess(SomeResource resource) {
                if(isVisible()){
                    doRetrievalPostProcessing(resource);
                }
            }

            @Override
            void onResourceRetrieveFailed(CommunicatorException e) {
                if(isVisible()){
                    doExceptionHandling();
                }
            }
        };

Using the isVisible() works because this ensures that the fragment is still on the foreground, part of the Activity and ready to do the handling. This, however, doesn't help me to cover the case for when the Fragment is not visible, but still part of the current Activity, limiting my possibilities for loading the Fragment before showing it. (Activity is in the background, Fragment is on the Backstack, Fragment loaded by a ViewPager).

Is there a better way to ensure that the Fragment is still on the current Activity's scope before I do the postprocessing? Maybe checking isAdded()?

This question seems to explain a broader but related situation, but it has no answers.

Thank you!

Upvotes: 2

Views: 390

Answers (1)

Budius
Budius

Reputation: 39856

there're two usual approaches to this case:

  1. the best approach is to have a way to clear the interface instance from the SomeInternetCommunicator. Something like:

.

@Override public void onDestroyView(){
   SomeInternetCommunicator.removeMe(this);
}
  1. if option (1) is not possible because SomeInternetCommunicator is a poorly coded library, you force option 1 to be possible by making a separate class to implement the interface.

.

public class SomeInternetCommunicatorInterceptor implements SomeInternetCommunicatorInterface {

    private SomeInternetCommunicatorInterface listener;

    public void setListener(SomeInternetCommunicatorInterface listener){
        this.listener=listener;
    }

    @Override
    public void onResourceRetrieveSuccess(SomeResource resource){
         if(listener != null){
              listener.onResourceRetrieveSuccess(resource);
         }
    }
    @Override
    public void onResourceRetrieveFailed(CommunicatorException e){
         if(listener != null){
              listener.onResourceRetrieveFailed(e);
         }
    }
}

then on your fragment you use this new class as:

private SomeInternetCommunicatorInterceptor interceptor;

public void onCreateView(....){

... your code

   interceptor = new SomeInternetCommunicatorInterceptor();

}

onStart(){
   interceptor.setListener(this);
}

onStop(){
   interceptor.setListener(null);
}

public void onDestroyView(){
   interceptor = null;
   super.onDestroyView();
}

VERY IMPORTANT:

if you make SomeInternetCommunicatorInterceptor an internal class of the fragment you HAVE TO make it as static class

Upvotes: 2

Related Questions