Firebase API 和 Android 的 activity 生命周期

Firebase API and Android's activity lifecycle

Firebase API 是围绕回调架构设计的。它充满了类似这个 Firestore 示例的结构(取自 here):

db.collection("cities").document("DC")
        .delete()
        .addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                Log.d(TAG, "DocumentSnapshot successfully deleted!");
            }
        })
        .addOnFailureListener(new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                Log.w(TAG, "Error deleting document", e);
            }
        });

或这个 Firebase 身份验证示例(来自 here):

AuthUI.getInstance()
    .signOut(this)
    .addOnCompleteListener(new OnCompleteListener<Void>() {
        public void onComplete(@NonNull Task<Void> task) {
            // ...
        }
    });

Google 自己的示例代码和应用程​​序充满了此类回调(通常作为侦听器分配给 Task 对象),总是直接编码在 activity 或片段中。

对于iOS和web编程来说,这种基于回调的API真的很不错。这对 Android 也很好,除了它似乎与 Google 自己关于处理 activity(和片段)生命周期的指南相反。具体来说,对 API 方法(如 delete()signOut() 的调用是异步操作的。作为侦听器传递的回调对象将被保留,直到关联的任务完成并调用回调。如果 activity(或片段)在任务执行时被销毁然后重新创建(例如,通过配置更改),则将在陈旧对象上调用回调。回调还保留对已销毁 activity 的引用,导致内存泄漏。

我们应该如何处理这些回调?对于观察查询,Google 建议使用 LiveData 对象(如 this blog post)。但是,对于许多 Firebase 任务(例如上面的两个示例),使用 LiveData 没有多大意义。我们是否应该将所有这些调用编码在保留的片段或服务中?这似乎不太可行。另一方面,也许这是唯一合理的做法(考虑到 Task 对象在添加后无法删除侦听器)。

当使用接受 Activity 参数作为第一个参数的 addOnCompleteListener、addOnSuccessListener 和 addOnFailureListener 的 Task API, if you have work that should be bound to the lifecycle of an Activity, you should be using the activity-scoped overloads 时。当你这样做时,回调将在 activity 停止时自动删除,并且回调对象不会泄漏(除非你做错了什么)。

对于我所知道的大多数任务实现,像这样添加和删除侦听器几乎没有成本,因为 SDK 不会通过在内部缓存结果有效地重复工作。

如果您的工作确实必须持续监听以获得结果,或者您不相信 SDK 在添加自动删除监听器时会做正确的事情,那么 LiveData 来自架构组件可以帮助保留跨生命周期变更的工作。