由于异步Firebase调用,主线程做了太多工作?

时间:2016-05-30 19:00:53

标签: java android multithreading android-asynctask firebase

我的应用程序上出现错误I/Choreographer: Skipped 252 frames! The application may be doing too much work on its main thread.我认为这会导致我的UI出现一些我不想要的延迟。我认为这是因为当我执行Firebase查询时,当我执行onDataChange()时,它似乎总是在主UI线程中执行。我有大约5个类似于以下内容的Firebase查询。因此,我尝试将代码从onDataChange()方法移至AsyncTask并更新onPostExecute() AsyncTask方法上的UI线程。但是,当我尝试这个时,onPostExecute()方法永远不会完成。这是我的尝试:

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) {
        }
    });
}

任何帮助都会有所帮助。如果有人能帮助我,这将是伟大的。谢谢!

编辑:添加功能以创建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功能:

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);
        }
    });
}

7 个答案:

答案 0 :(得分:3)

您的主线程可能很慢,因为您正在运行包含许多对象的循环。但是,不知道你究竟在执行什么,这只是猜测。

但是我的AsyncTask存在问题,我认为它不适用于您发布的代码。

AsyncTask期待一系列URL。你没有通过任何。如果不需要URL作为输入,那么只需使用:

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

您尝试进行问题排查:

    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);
                }
            }
        }
    };

然后通过触发执行任务:

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

使用IDE中的调试功能,如果代码失败,则回发确切的位置。

答案 1 :(得分:1)

您没有在您创建的AsyncTask上调用execute(),因此永远不会被触发。

在创建AsyncTask之后,在其上调用execute(URL ... params)以便它将被执行。

此外,我发现你没有使用你传递的参数,所以你可以不通过它,它会起作用。

答案 2 :(得分:1)

您最好在IntentService上进行网络请求。它很容易并且防止界面冻结和/或“应用程序可能在其主线程上做了太多工作”。

看看:

IntentService on Android Developer Documentation

答案 3 :(得分:1)

在你的代码中你正在调用AsynTast,你应该调用一次for:for each循环应该在doinbackground()内部和onProgressUpdate()中你可以更新UI而不是onPostExecute(),这稍微改进代码但不完全,你应该在onPreexecute上使用进度条并在onPostEcecute()中关闭进度条,只是为了你的想法我写了下面的代码,但是没有完全引用它,因为我还没有测试过这个,但形成这个会有所了解。

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().equals("Public") && application.getPublicAdapter().containsId(publicPosts, post.getId()) == null) {
                        application.getPublicAdapter().getPosts().add(0, post);
                    }
              publishProgress((1);
      }
                    return null;
                }
                @Override
                protected void onProgressUpdate(Integer... progress) {
                  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);
                }
            }
                }

                @Override
                protected void onPostExecute(Long result) {

                    }
                }
            };

答案 4 :(得分:1)

如果你创建了一个扩展AsyncTask的类并在你的ondatachange方法中调用它,它会更清晰,更容易维护:

import

通过这种方式,您可以在工作线程中安全地执行Firebase连接,并使用您检索到的结果更新UI。

答案 5 :(得分:0)

您可以尝试以下某些操作:

  • 不是创建和执行多个N AsyncTasks,而是创建并执行一个内部有N个快照循环的单个。
  • 在onPostExecute()中检查 populateNewsFeedList 方法是否正在进行密集数据操作,如果是这样,您也可以在新的AsyncTask中执行它并在完成后更新UI。
  • 您还可以尝试通过将runnable传递给post方法来更新视图,这会将主要线程消息队列中的runnable排入队列。
  • 您可以在服务 IntentService 中运行长时间运行的操作,并通过 ContentProvider 广播<更新您的用户界面/强>
希望有所帮助。

答案 6 :(得分:0)

建议:

  1. 从UI主线程中删除繁重的工作,在其他线程中执行它们。
  2. 2.当其他线程完成繁重的工作时,如果需要更新UI,请使用Handler将msg发送到UI主线程,然后在主线程上更新UI。