Bosen
Bosen

Reputation: 941

Call method in another thread on completion

Currently, I am using firebase Realtime Database. Hence, my data changes come from another thread. Hence, I have no control on when the fresh data update comes over. How do I then know when to call to refresh my UI?

This is my implementation of a swipe to delete in a RecyclerView.

    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        int pos = viewHolder.getAdapterPosition();
        mListRoute.get(pos).removeRoute();
        refreshUI();
    }

This is the removeRoute() method found in my Route class

 public void removeRoute() {
        //Delete all the Route Instructions
        DatabaseReference mRef = FirebaseDatabase.getInstance().getReference()
                .child("Routes")
                .child(routeId)
                .child("Route Instructions");

        mRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                for (DataSnapshot routeInstruc : dataSnapshot.getChildren()) {
                    routeInstruc.getValue(RouteInstructions.class)
                            .removeRouteInstructions();
                }

                DatabaseReference mRef2 = FirebaseDatabase.getInstance().getReference()
                        .child("Routes")
                        .child(routeId);
                mRef2.removeValue();
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
    }

As you can see, the onDataChange() is called by another thread. Which means i do not know when to call my refreshUI() method.

I think i might be able to use a Looper but how do i fit that in the Route class?

Thanks

Upvotes: 0

Views: 921

Answers (2)

Ricardo Vieira
Ricardo Vieira

Reputation: 1818

What you are looking for are callbacks.

Callbacks are practically mandatory when dealing with asynchronous calls, because when you call an asynchronous task, you are basically asking a worker thread to work for you.
It may take 1 second, 10 seconds, 10 minutes, etc, and you can not know for sure. What you can do is delegate that same worker thread and tell her "hey, reply back when you finish the task I gave you".
Enter the callbacks!

You can check for more documentation regarding callbacks here

Say that you have your query defined with the ValueEventListener

query.addListenerForSingleValueEvent(new ValueEventListener() {
   @Override
   public void onDataChange(DataSnapshot data) {

   }
}

What you should do is have a callback method that replies back as soon as the query listener returns a value (in other words, when your query is executed). So, have a method like 'onResponseReceivedFromFirebase' and implement it on the callback

public class MyActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    // Implement your callback here with the parameters you want (for instance, I used a String there)
    public void onResponseReceivedFromFirebase(String argument){
        Log.d(MyActivity.class,argument);
    }

    ....

    query.addListenerForSingleValueEvent(new ValueEventListener() {
       @Override
       public void onDataChange(DataSnapshot data) {
            onResponseReceivedFromFirebase("the response arrived!");
       }
    }

    ...

}

@Edit Base on your updated code, I would proceed with the following

@Override
public void onDataChange(DataSnapshot dataSnapshot) {
    for (DataSnapshot routeInstruc : dataSnapshot.getChildren()) {
         routeInstruc.getValue(RouteInstructions.class)
        .removeRouteInstructions();
    }
    DatabaseReference mRef2 = FirebaseDatabase.getInstance().getReference()
                    .child("Routes")
                    .child(routeId);
            mRef2.removeValue();
   // Implement the callback here 
   MyActivity.this.onResponseReceivedFromFirebase("We have received a response from   dataChanged");
}

@Edit 2 : On Frank van Puffelen's remark, the onDataChange method already runs on the Main Thread, thus allowing you to change any element on the UI.

Very import : If the processing payload of the outcoming data is considerably large, you should pass that same processing into another thread (e.g. An AsyncTask) to avoid making your app non-responsive.

Upvotes: 1

Frank van Puffelen
Frank van Puffelen

Reputation: 598728

While the Firebase client handles all network and disk IO on a separate thread, it invokes the callback to your code on the main thread. So you can update the UI straight from onDataChange(), without having to worry about the thread yourself.

In fact, all examples in the Firebase documentation update the UI from their onDataChange() (or onChild...()) callbacks. One example from the database quickstart:

public void onDataChange(DataSnapshot dataSnapshot) {
    // Get Post object and use the values to update the UI
    Post post = dataSnapshot.getValue(Post.class);
    mAuthorView.setText(post.author);
    mTitleView.setText(post.title);
    mBodyView.setText(post.body);
}

Upvotes: 2

Related Questions