【问题标题】:Firebase Listener freezes app uiFirebase 侦听器冻结应用程序 ui
【发布时间】:2018-01-01 05:07:55
【问题描述】:

我的应用程序实现了一个 ChildEventListener 来将数据加载到一个 ArrayList(大约 7000 个项目)中。 在对每个项目执行 childAdded 期间,界面完全冻结,无法使用。 有什么方法可以在后台运行它并且不会影响可用性? 我已经尝试过使用 AsyncTask 和 Thread 但应用程序仍然冻结。提前致谢。

类 FBTask 扩展 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() {

}

}

【问题讨论】:

    标签: android firebase firebase-realtime-database


    【解决方案1】:

    Firebase 客户端所做的所有网络交互和其他工作都已经发生在主线程之外。主线程上发生的唯一事情是对您的代码的回调,例如onChildAdded()。这样做是为了让您可以从该代码更新您的 UI。

    我的猜测是调用dataSnapshot.getValue(Model.class) 7000 次会占用太多时间,这会导致帧被跳过。你真的需要7000个模型吗?我通常建议只检索您要直接向用户显示的数据,而且 7000 种模型听起来比大多数 Android 设备的屏幕都合理。

    如果您确实必须检索和解码这么多项目,则需要使用AsyncTaskbackground service。如果您无法完成这些工作,请分享minimal code that reproduces where you got stuck

    【讨论】:

    • 我在 asyncTask 中尝试过代码,我什至使用了辅助数组,一旦加载了所有元素,就会将辅助数组加载到主数组中,但它仍然冻结
    • 您对dataSnapshot.getValue(Model.class) 的所有调用仍在主线程上进行。为了防止这种情况,您需要在onChildAdded() 回调内部创建后台任务。这样做 7000 次也不容易,所以我建议使用后台服务来卸载工作。最好的解决方案保持不变:不要请求 7000 个项目,或者至少一次。
    • 我是否将 Listener 放入服务中?或者我必须在 OnChildAdded 中运行服务
    • onChildAdded 将在主线程上被调用。所以你需要把工作从那里卸载到一些后台机制。
    【解决方案2】:

    每个回调都由您的主线程(UI 线程)处理。因为您有大量项目(7000 个项目),所以会创建数组,从小到大的数组列表复制项目是在运行时发生的。这会导致 ANR(冻结您的应用程序)。为避免这种情况,您可以简单地使用新线程在数组列表中添加项目。当您完成添加所有项目时,进行线程间通信(通知主线程),以便主线程完成进一步的工作。这是确切的解决方案。我过去曾解决过类似的问题。

    【讨论】:

    • 你没有按照我说的去做。您正在 doInBackGround() 中查询您的 NoSQL 数据库。请注意,doInBackground 在 UI 线程中运行。此外,远程数据库查询是一个长期运行的过程。 AsyncTask 不适合这个。唯一的解决方案是您必须使用工作线程。 stackoverflow.com/questions/12797550/…
    • 我认为 AsyncTask.doInBackground () 方法在与 uiThread 不同的线程上运行,因此您必须使用其他 AsyncTask 方法来修改 ui,但将其与此类一起使用是个坏主意一项漫长的任务
    • AsyncTask doInBackground 在单独的线程中运行,但它仍然通过线程池执行器与您的 UI 线程在内部绑定。所以不要将 AsyncTask 用于长时间运行的任务。感谢您指出错误。
    • @Benkasem 阅读了链接的问题,但不明白当时使用哪个类,Executor、Executors、Handler?很长的 new Handler()... 对我来说似乎已经足够了,你能提供一个例子吗
    • @cutiko 我现在使用服务但屏幕仍然冻结,有什么更好的方法?感谢您的宝贵时间。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-31
    • 2019-04-10
    • 1970-01-01
    • 1970-01-01
    • 2017-02-22
    • 1970-01-01
    相关资源
    最近更新 更多