首页 > 解决方案 > 在 Firebase 实时数据库上等待的先前任务需要先完成,然后才能启动新任务

问题描述

我在我的应用程序中使用任务 API从 Firebase 数据库中检索数据,该数据库通常来自不同的节点。我有一个 Firebase 数据库的辅助类,如下所示:

public class FirebaseDbHelper {

    public Task<DataSnapshot> getData() {
        TaskCompletionSource<DataSnapshot> source = new TaskCompletionSource<>();
        DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference(FIRST_NODE).child(SUB_NODE);
        dbRef.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                source.setResult(dataSnapshot);
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                source.setException(databaseError.toException());
            }
        });
        return source.getTask();
    }

}

如您所见,getData()返回一个 Task 对象,我在我的交互器类中使用它(我正在为我的应用程序使用 MVP 架构),如下所示:

public class TestDbInteractor {

    private FirebaseDbHelper mDbHelper;
    private Listener mListener;

    public TestDbInteractor(@NonNull Listener listener) {
        mDbHelper = new FirebaseDbHelper();
        mListener = listener;
    }

    void getData() {
        mDbHelper.getData().addOnCompleteListener(task -> {
            if (task.isSuccessful()) {
                mListener.onGetDataSuccess(new MyObject(task.getResult()));
            } else {
                mListener.onGetDataFailed(task.getException());
            }
        });
    }

    public interface Listener {

        void onGetDataSuccess(MyObject object);

        void onGetDataFailed(Exception exception);

    }

}

这按预期工作。但是,我们注意到在检索大量数据时,即使启动任务的活动已经被finish()编辑,任务仍然继续并尝试完成。我相信,这可以被视为内存泄漏,因为即使进程应该已经停止/销毁,它仍在继续。

更糟糕的是,当我尝试获取不同的数据时(在不同的活动中使用不同的任务到 Firebase 中的不同节点),我们注意到它会等待前一个任务首先完成,然后再继续执行这个新任务。

为了提供更多背景信息,我们正在开发一个类似于 Telegram 的聊天应用程序,用户可以在其中拥有多个房间,并且我们看到的行为发生在用户进入房间时。这是流程:

  1. 用户进入房间,我请求房间详细信息的数据。
  2. 获得房间详细信息后,我显示它,然后请求消息。我只检索最近的 10 个。在此期间,我只是在活动上显示一个进度条。

为了使消息详细信息完整,我从 Firebase 上的不同节点获取数据,这是我主要使用任务的地方。

  1. 收到消息后,我将其传递给 View,以显示消息,然后为新消息附加一个侦听器。一切都按预期工作。

当用户执行以下操作时,我在开头提到的行为很明显:

  1. 用户进入带有消息的房间,立即检索房间详细信息,消息仍在加载中。
  2. 用户离开房间(按下后退按钮),这让用户回到房间列表,并进入另一个房间。

在这一点上,房间详细信息的检索需要很长时间——我们认为这很奇怪,因为一开始数据并没有那么大。

经过几次测试,我们得出的结论是,检索时间过长是由于当前任务(获取房间详细信息)仍在等待上一个任务(获取消息)在不同的活动中启动,先完成后再启动。

我试图在这里实现我的答案,尝试使用 a CancellableTask,但我不知道如何在我当前的实现中使用它,我使用 a TaskCompletionSource,你只能设置结果或异常。

我想如果我将任务完成源移动到交互器类级别而不是助手级别,这可能会起作用——我还没有尝试过。我认为这是可能的,但是重构我已经拥有的类需要很多时间。

所以我想为什么不使用活动范围的侦听器尝试Doug 的答案。所以我像下面这样测试它。

在我的activity中,我添加了一个getActivity()方法,可以在presenter中调用:

public class TestPresenter
        implements TestDbInteractor.Listener {

    private View mView;

    private TestDbInteractor mDbInteractor;

    @Override
    void bindView(View view) {
        mView = view;

        mDbInteractor = new TestDbInteractor(this);
    }

    @Override
    void requestMessages() {
        mDbInteractor.getData(mView.getActivity());
    }

    // Listener stuff below

}

并像这样更新了我getData()的:

void getData(@NonNull Activity activity) {
    mDbHelper.getData().addOnCompleteListener(activity, task -> {
        if (task.isSuccessful()) {
            mListener.onGetDataSuccess(new MyObject(task.getResult()));
        } else {
            mListener.onGetDataFailed(task.getException());
        }
    });
}

不幸的是,这似乎不起作用,退出活动仍然等待任务完成,然后在不同活动中启动的新任务开始。

标签: androidfirebasefirebase-realtime-databasegoogle-tasks-api

解决方案


如果您启动对实时数据库的查询,它将始终运行到完成,无论是否有任何侦听器附加到返回的任务。没有办法取消这项工作,无论是手动删除最后一个侦听器,还是使用自动删除的活动范围侦听器。动态查询保持动态。 此外,所有进出 RTDB 的流量都通过单个套接字进行管道传输,这意味着在一个不完整的查询之后的后续查询的结果将不得不等待队列中它前面的所有内容首先完成。 这可能是您观察到的根本原因 - 无论您使用 Task API,您的查询都不完整,其他查询正在等待。

幸运的是,如果您启用了持久性,则第二个查询应该由第一个查询的缓存提供服务,并且不需要再次往返服务器。

如果您需要确保在破坏活动的配置更改中保留第一个查询的结果,那么您应该使用Android 架构组件中的LiveData之类的东西来管理它,这样您就可以在它离开的地方提取查询配置更改后关闭。如果您这样做,请不要使用活动范围的侦听器。

我写了一篇关于在 Firebase 中使用架构组件的三部分博客文章,这可能也很有趣。


推荐阅读