title: Previous task(s) waiting on Firebase Realtime Database need to complete first before new one starts
body: <p>I'm using the <a href="https://developers.google.com/android/guides/tasks" rel="nofollow noreferrer">Task API</a> in my app to retrieve data from Firebase Database, which is usually from different nodes. I have a helper class for Firebase Database like so:</p>
  
  <pre><code>public class FirebaseDbHelper {
  
      public Task&lt;DataSnapshot&gt; getData() {
          TaskCompletionSource&lt;DataSnapshot&gt; source = new TaskCompletionSource&lt;&gt;();
          DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference(FIRST_NODE).child(SUB_NODE);
          dbRef.addListenerForSingleValueEvent(new ValueEventListener() {
              @Override
              public void onDataChange(DataSnapshot dataSnapshot) {
                  source.setResult(dataSnapshot);
              }
  
              @Override
              public void onCancelled(DatabaseError databaseError) {
                  source.setException(databaseError.toException());
              }
          });
          return source.getTask();
      }
  
  }
  </code></pre>
  
  <p>As you can see, <code>getData()</code> returns a Task object, which I use on my interactor class (I'm using the MVP architecture for my app) like so:</p>
  
  <pre><code>public class TestDbInteractor {
  
      private FirebaseDbHelper mDbHelper;
      private Listener mListener;
  
      public TestDbInteractor(@NonNull Listener listener) {
          mDbHelper = new FirebaseDbHelper();
          mListener = listener;
      }
  
      void getData() {
          mDbHelper.getData().addOnCompleteListener(task -&gt; {
              if (task.isSuccessful()) {
                  mListener.onGetDataSuccess(new MyObject(task.getResult()));
              } else {
                  mListener.onGetDataFailed(task.getException());
              }
          });
      }
  
      public interface Listener {
  
          void onGetDataSuccess(MyObject object);
  
          void onGetDataFailed(Exception exception);
  
      }
  
  }
  </code></pre>
  
  <p>This works as expected. However, we noticed a behavior that when retrieving a lot of data, even if the activity that started the task is already <code>finish()</code>ed, the task still proceeds and attempts to complete. This I believe, is something that could be considered as a memory leak, since a process is still going even though it's supposed to be stopped/destroyed already.</p>
  
  <p>What's worse is that when I try to get a different data (using a different Task in a different activity to a different node in Firebase), we noticed that it waits for the previous task to complete first before proceeding with this new one.</p>
  
  <p>To give more context, we're developing a chat app similar to Telegram, where users could have multiple rooms and the behavior we saw is happening when a user enters a room. This is the flow:</p>
  
  <ol>
  <li>User enters room, I request data for the room details.</li>
  <li>Upon getting the room details, I display it, then request for the messages. I only retrieve the most recent 10. During this time, I just show a progress bar on the activity.</li>
  </ol>
  
  <p>In order for the message details to be complete, I get data from different nodes on Firebase, this is where I use Tasks mainly.</p>
  
  <ol start="3">
  <li>After getting the messages, I pass it on to the View, to display the messages, then I attach a listener for new messages. Everything works as expected.</li>
  </ol>
  
  <p>The behavior I mentioned at the beginning is noticeable when the user does something like this:</p>
  
  <ol>
  <li>User enters a room with messages, room details are retrieved instantly, messages are still loading.</li>
  <li>User leaves the room (presses the back button), this gets the user back to the room list, and enters a different one.</li>
  </ol>
  
  <p>At this point, the retrieval of the room details takes such a long time - which we thought was odd, since the data isn't really that big to begin with.</p>
  
  <p>After a few more testing, we concluded that the long retrieval time was caused by the current task (get room details) is still waiting for the previous task (get messages) started in a different activity, to finish first before starting.</p>
  
  <p>I attempted to implement my answer <a href="https://stackoverflow.com/a/43464509/4625829">here</a>, trying to use a <code>CancellableTask</code>, but I am at a loss on how to use it with my current implementation, where I use a <code>TaskCompletionSource</code>, where you could only set a result or an exception.</p>
  
  <p>I was thinking this could work if I move the task completion source to the interactor class level instead of the helper -- I haven't tried it yet. I think it's possible, but would take a lot of time to refactor the classes I already have.</p>
  
  <p>So I figure why not try <a href="https://stackoverflow.com/a/43478082/4625829">Doug's answer</a>, using activity-scoped listeners. So I tested it like below.</p>
  
  <p>In my activity, I added a <code>getActivity()</code> method, which can be called in the presenter:</p>
  
  <pre><code>public class TestPresenter
          implements TestDbInteractor.Listener {
  
      private View mView;
  
      private TestDbInteractor mDbInteractor;
  
      @Override
      void bindView(View view) {
          mView = view;
  
          mDbInteractor = new TestDbInteractor(this);
      }
  
      @Override
      void requestMessages() {
          mDbInteractor.getData(mView.getActivity());
      }
  
      // Listener stuff below
  
  }
  </code></pre>
  
  <p>and updated my <code>getData()</code> like so:</p>
  
  <pre><code>void getData(@NonNull Activity activity) {
      mDbHelper.getData().addOnCompleteListener(activity, task -&gt; {
          if (task.isSuccessful()) {
              mListener.onGetDataSuccess(new MyObject(task.getResult()));
          } else {
              mListener.onGetDataFailed(task.getException());
          }
      });
  }
  </code></pre>
  
  <p>Unfortunately, this doesn't seem to work though, exiting the activity still waits for the tasks to complete, before the new task initiated in a different activity starts.</p>