0

My app implements a ChildEventListener to load the data into an ArrayList (approximately 7000 items). During childAdded execution for each item, the interface freezes completely and can not be used. Is there any way to run it in the background and that does not impair usability? I've already tried using an AsyncTask and a Thread but the app freezes anyway. Thanks in advance.

class FBTask extends AsyncTas {

@Override
protected Boolean doInBackground(final Boolean... params){
    int size = 7000; //aprox,
    final ArrayList<Model> aux = new ArrayList<>();
    Query db = FirebaseDatabase.getInstance().getReference()
            .child("List").orderByChild("Double");
    ChildEventListener cEL = new ChildEventListener() {
        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {
            Model x = dataSnapshot.getValue(Model.class);
            if(x.getT()!=null) {
                aux.add(x)
                Log.i("onChildAdded", x.getId() + " Added, pos: " + dX.size());
                if(aux.size()>=size) {
                    data = aux;
                }
            }
        }

        @Override
        public void onChildChanged(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onChildRemoved(DataSnapshot dataSnapshot) {

        }

        @Override
        public void onChildMoved(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    };
    db.addChildEventListener(cEL);
}

@Override
protected void onProgressUpdate(Boolean... values) {

}

@Override
protected void onPreExecute() {

}

@Override
protected void onPostExecute(DownAdapter result) {
    if(result != null) {
        DownActivity.downRecView.setAdapter(result);
    }
}

@Override
protected void onCancelled() {

}

}

Benkasem
  • 1
  • 2

2 Answers2

3

All network interaction and other work the Firebase client does already happens off the main thread. The only things that happens on the main thread are callbacks to your code, such onChildAdded(). This is done so you can update your UI from that code.

My guess is that calling dataSnapshot.getValue(Model.class) 7000 times is taking too much times, which is causing frames to be skipped. Do you really need 7000 models? I'd normally recommend to only retrieve data that you're going to show directly to the user, and 7000 models sounds like more than could reasonably fit on screen for most Android devices.

If you really must retrieve and decode that many items, you will need to use a AsyncTask or a background service. If you're having trouble making those work, share the minimal code that reproduces where you got stuck.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
  • I have tried the code in an asyncTask and I have even used an auxiliary array and once loaded all the elements load the auxiliary in the main array but it is still freezing – Benkasem Jul 26 '17 at 05:14
  • All your calls to `dataSnapshot.getValue(Model.class)` are still happening on the main thread. To prevent that, you need to create the background task **inside** the `onChildAdded()` callback. Doing that 7000 times is not going to be easy either, so I recommend using a background service to offload the work. The best solution remains the same though: don't request 7000 items, or at the very least at once. – Frank van Puffelen Jul 26 '17 at 14:39
  • Do I put the Listener in the service? Or I have to run a service inside the OnChildAdded – Benkasem Jul 26 '17 at 23:13
  • `onChildAdded` will get invoked on the main thread. So you need to offload the work from there to some background mechanism. – Frank van Puffelen Jul 26 '17 at 23:15
0

Every callbacks are handled by your Main Thread ( UI thread). Because you have large number of items (7000 items), there is array creation, copy of items from smaller to large array list is happening in runtime. This is causing ANR ( freeze your app). To avoid this, you can simply use new thread to add items in the array list. when you complete adding all items, do inter thread communication ( notify main thread) so that main thread does the further work. This is the exact solution. I had solved in the past the similar problem.

Uddhav P. Gautam
  • 7,362
  • 3
  • 47
  • 64
  • You have not done what I told. You are querying your NoSQL database inside doInBackGround(). Please note, doInBackground runs in UI thread. Further, remote database querying is a long running process. AsyncTask is not suitable for this. The only solution for this is you have to use worker thread. https://stackoverflow.com/questions/12797550/android-asynctask-for-long-running-operations – Uddhav P. Gautam Jul 26 '17 at 05:33
  • I think that the AsyncTask.doInBackground () method runs on a different thread than uiThread, so you have to use the other AsyncTask methods to modify the ui but it is a bad idea to use it with such a long task – Benkasem Jul 26 '17 at 06:09
  • AsyncTask doInBackground runs in separate thread but still it is internally tied with your UI thread via thread pool executor. So don't use AsyncTask for long running tasks. Thanks for pointing out mistakes. – Uddhav P. Gautam Jul 26 '17 at 06:21
  • @Benkasem read the linked question but didn't understand which class use then, Executor, Executors, Handler? Something a long the lines of new Handler()... seems enough for me, can you please provide an example – cutiko Jul 26 '17 at 19:43
  • @cutiko I use a Service now but the screen still freezing, what is the better way? Thanks for your time. – Benkasem Mar 10 '18 at 17:32