Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Main thread doing too much work because of asynchronous Firebase calls?

I keep getting an error on my application that says I/Choreographer: Skipped 252 frames! The application may be doing too much work on its main thread. I think this causes some lag on my UI which I don't want. I think it is because when I perform a Firebase query, when I do onDataChange(), it seems to be always executed in the main UI Thread. I have around 5 Firebase queries similar to what I have below. As a result, I tried to move my code from onDataChange() method to an AsyncTask and update the UI thread on onPostExecute() method of AsyncTask. However, when I try this, the onPostExecute() method never completes. Here is my attempt:

public void getPublicPosts(final View progressOverlay, final View fragmentView, final Context context) {
    //Need to do order by / equal to.
    Firebase postsRef = firebaseRef.child("Posts");
    Query query = postsRef.orderByChild("privacy").equalTo("Public");
    query.keepSynced(true);
    query.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            for (final DataSnapshot postSnapShot : dataSnapshot.getChildren()) {
                AsyncTask task = new AsyncTask<URL, Integer, Long>() {
                    @Override
                    protected Long doInBackground(URL... params) {
                        Post post = postSnapShot.getValue(Post.class);
                        List<Post> publicPosts = application.getPublicAdapter().getPosts();
                        if (post.getPrivacy().equals("Public") && application.getPublicAdapter().containsId(publicPosts, post.getId()) == null) {
                            application.getPublicAdapter().getPosts().add(0, post);
                        }
                        return null;
                    }
                    @Override
                    protected void onProgressUpdate(Integer... progress) {
                    }

                    @Override
                    protected void onPostExecute(Long result) {
                        System.out.println("Finished executing public");
                        populateNewsFeedList(fragmentView, application.getPublicAdapter(), TabEnum.Public, context);
                        if (progressOverlay.getVisibility() == View.VISIBLE) {
                            System.out.println("getPublicPosts: DONE");
                            AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200);
                            fragmentView.findViewById(R.id.rv_public_feed).setVisibility(View.VISIBLE);
                        }
                    }
                };
                `task.execute();`
            }
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
        }
    });
}

Any help with this would be helpful. If anyone could help me out that would be great. Thanks!

EDIT: Adding in Function to create AsyncTask

public AsyncTask asyncTaskWrapper(final DataSnapshot dataSnapshot, final View progressOverlay, final View fragmentView, final Context context) {
        AsyncTask task = new AsyncTask<URL, Integer, Long>() {
            @Override
            protected Long doInBackground(URL... params) {
                for (final DataSnapshot postSnapShot : dataSnapshot.getChildren()) {
                    Post post = postSnapShot.getValue(Post.class);
                    List<Post> publicPosts = application.getPublicAdapter().getPosts();
                    if (post.getPrivacy() == PrivacyEnum.Public && application.getPublicAdapter().containsId(publicPosts, post.getId()) == null) {
                        application.getPublicAdapter().getPosts().add(0, post);
                    }
                }
                return null;
            }
            @Override
            protected void onProgressUpdate(Integer... progress) {
            }

            @Override
            protected void onPostExecute(Long result) {
                System.out.println("Finished executing public");
                TabsUtil.populateNewsFeedList(fragmentView, application.getPublicAdapter(), TabEnum.Public, context);
                if (progressOverlay.getVisibility() == View.VISIBLE) {
                    System.out.println("getPublicPosts: GONE");
                    AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200);
                    fragmentView.findViewById(R.id.rv_public_feed).setVisibility(View.VISIBLE);
                }
            }
        };
        return task;
    }

PublicPosts function:

public void getPublicPosts(final View progressOverlay, final View fragmentView, final Context context) {
    //Need to do order by / equal to.
    Firebase postsRef = firebaseRef.child("Posts");
    Query query = postsRef.orderByChild("privacy").equalTo(PrivacyEnum.Public.toString());
    query.keepSynced(true);
    query.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            asyncTaskWrapper(dataSnapshot, progressOverlay, fragmentView, context);
        }

        @Override
        public void onCancelled(FirebaseError firebaseError) {
            TabsUtil.populateNewsFeedList(fragmentView, application.getPublicAdapter(), TabEnum.Public, context);
        }
    });
}
like image 515
user1871869 Avatar asked May 30 '16 19:05

user1871869


1 Answers

Your main thread is probably slow because you are running a loop with many objects. However, not knowing what exactly you are executing, that is just a guess.

But I a problem with your AsyncTask, I don't think it will ever work with the code you posted.

The AsyncTask is expecting an array of URLs. You are not passing any. If don't need URLs as input, then just use:

AsyncTask task = new AsyncTask<Void, Boolean, Boolean>() 

How about you try for troubleshooting:

    AsyncTask task = new AsyncTask<URL, Boolean, Boolean>() {
        @Override
        protected Boolean doInBackground(URL... params) {
            for (final DataSnapshot postSnapShot : dataSnapshot.getChildren()) {
                Post post = postSnapShot.getValue(Post.class);
                List<Post> publicPosts = application.getPublicAdapter().getPosts();
                if (post.getPrivacy() == PrivacyEnum.Public && application.getPublicAdapter().containsId(publicPosts, post.getId()) == null) {
                    application.getPublicAdapter().getPosts().add(0, post);
                }
            }
            return true;
        }

        @Override
        protected void onPostExecute(Boolean result) {
            if(result){
                System.out.println("Finished executing public");
                TabsUtil.populateNewsFeedList(fragmentView, application.getPublicAdapter(), TabEnum.Public, context);
                if (progressOverlay.getVisibility() == View.VISIBLE) {
                    System.out.println("getPublicPosts: GONE");
                    AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200);
                    fragmentView.findViewById(R.id.rv_public_feed).setVisibility(View.VISIBLE);
                }
            }
        }
    };

Then execute the task by firing:

URL[] urls = new URL[2];
urls[0] = new URL(...);
urls[1] = new URL(...);
task.execute(urls);

Use the debug feature in the IDE and post back the exact place if the code fails.

like image 170
Dmitri Borohhov Avatar answered Sep 20 '22 06:09

Dmitri Borohhov