5

This is my first post so if I didn't follow some protocol I was supposed to, apologies.

I am trying to populate a ListView with some information from my Firebase database. I think the problem I am having is that the query to the database is too slow (the thread is probably downloading pictures) and my activity loads its activity layout without waiting for the thread to finish executing. (If I step through the debugger and wait a bit, I will eventually see the information I am parsing: user names, user numbers, and user pictures) Everything I have queried suggests I should use AsyncTask to accomplish this. As opposed to using thread blocking or a semaphore b/c AsyncTask is thread safe.

To my understanding, Firebase queries are already executing asynchronously; therefore, the doInBackground method for AsyncTask I have "tried" to implement seems redundant. Also, I am a bit confused of AsyncTask's overloaded signature and the call to: new someTask.execute("some stuff in a string").

Any suggestions on how I can accomplish this? Any feedback is very much appreciated!

// Please ignore the minor indentation from pasting my code in

protected void onCreate(Bundle savedInstanceState) {
    ...
    new getFirebaseInfoTask();
}

private class getFirebaseInfoTask extends AsyncTask {

    @Override
    protected Object doInBackground(Object... args) {
        // Do stuff
        userInfoList = GetUserInfoFromFirebase.getUserInfo();
        // Unsure if I need to return here.
        return userInfoList;
    }

    @Override
    protected void onProgressUpdate(Object... args) {
        // Update your UI here
        populateUserInfoList();
    }
}


private void populateUserInfoList() {
    // Create list of items
    Collections.addAll(userInfoList);
    populateFriendsListView();

}


private void populateFriendsListView() {
    // Build the adapter
    ArrayAdapter<UserInfo> adapter = new MyListAdapter();

    // Configure the list view
    ListView listView = (ListView) findViewById(R.id.friends_listview);
    listView.setAdapter(adapter);

    registerClickCallBack();
}

... // More code


public class GetUserInfoFromFirebase {

public static ArrayList getUserInfo() {
    final ArrayList<UserInfo> list = new ArrayList<UserInfo>();
    Firebase firebase = new Firebase("https:......firebaseio.com");
    firebase.child("users").addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot snapshot) {
            HashMap<String, Object> users = (HashMap<String, Object>) snapshot.getValue();
            for(Object user : users.values()) {
                HashMap<String, Object> userMap = (HashMap<String, Object>) user;
                String userNumber = (String) userMap.remove("number");
                if(!list.contains(userNumber)) {
                    String name = (String) userMap.remove("username");
                    String pic = (String) userMap.remove("profile_picture");
                    UserInfo info = new UserInfo(userNumber, name, pic);
                    list.add(info);
                }
            }
        }
        @Override
        public void onCancelled(FirebaseError firebaseError) {}
    });
    return list;
}
Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
coreyshort
  • 71
  • 1
  • 1
  • 6
  • 1
    So I figured it out. I got rid of the AsyncTask and moved the method call I wanted to execute in onProgressUpdate to outside of the for loop of my onDataChange such that the thread that actually gets access to the onDataChange method calls my populateFriendsView method. – coreyshort Dec 11 '14 at 07:26
  • Great to hear that you figured it out and thanks for reporting back. Can you provide that information as an answer (you might have to wait a bit before you're allowed to do that)? You can then also accept your own answer, which helps building your reputation score. – Frank van Puffelen Dec 11 '14 at 16:55
  • Firebase also has an OS lib that automates populating ListView: https://github.com/firebase/firebaseui-android – Kato Jan 20 '16 at 15:59

3 Answers3

2

So I figured it out. I got rid of the AsyncTask and moved the method call I wanted to execute in onProgressUpdate to outside of the for loop of my onDataChange such that the thread that actually gets access to the onDataChange method calls my populateFriendsView method.

private void populateUserInfoList() {
    userInfoList = new ArrayList<UserInfo>();
    firebase = new Firebase("https://....firebaseio.com");
    firebase.child("users").addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot snapshot) {
            HashMap<String, Object> users = (HashMap<String, Object>) snapshot.getValue();
            for (Object user : users.values()) {
                HashMap<String, Object> userMap = (HashMap<String, Object>) user;
                String userNumber = (String) userMap.remove("number");
                if (!userInfoList.contains(userNumber)) {
                    String name = (String) userMap.remove("username");
                    String pic = (String) userMap.remove("profile_picture");
                    UserInfo info = new UserInfo(userNumber, name, pic);
                    userInfoList.add(info);
                }
            }
            // thread executing here can get info from database and make subsequent call
            Collections.addAll(userInfoList); 
            populateFriendsListView();
        }
        @Override
        public void onCancelled(FirebaseError firebaseError) {
            String message = "Server error. Refresh page";
            Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
        }
    });
}
coreyshort
  • 71
  • 1
  • 1
  • 6
1

Firebase itself provide Asynchronous methods to query data , They have created a sample app to show How to backing up the Listview with firebase information through Android Chat

check this

In that example the core functionality is placed in a generic base adapter called FirebaseListAdapter...

sivaram636
  • 419
  • 3
  • 14
  • I did not have a chance to give this a in depth look, however, thank you! As I will undoubtedly give this a glance soon. Also, in regards to my previous solution, I came to the understanding that it would be better to use a listener on a condition variable in order to signal exactly when the asynchronous call to Firebase had finished. This would clearly be a better solution than nesting my method calls. – coreyshort Apr 24 '15 at 15:13
0

Snippet from my working code base

  Firebase firebase = new Firebase(Constants.FREEBASE_DB_URL);
  Firebase childRef = firebase.child("sessions");


  childRef.addValueEventListener(new ValueEventListener() {

                @Override
                public void onDataChange(DataSnapshot snapshot) {
                    System.out.println(snapshot.getValue());

                    Map<String, Session> td = (HashMap<String, Session>) snapshot.getValue();

                    List<Session> valuesToMatch = new ArrayList<Session>(td.values());

                    apiClientCallback.onSuccess(valuesToMatch);

                }

                @Override
                public void onCancelled(FirebaseError error) {

                    Toast.makeText(context, "onCancelled" + error.getMessage(), Toast.LENGTH_SHORT).show();
                }
            });
Hitesh Sahu
  • 41,955
  • 17
  • 205
  • 154