You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android直播应用调用Firebase Firestore读写时UI冻结问题

问题分析与解决方案

核心问题

你的UI冻结问题根源在于以下几点:

  1. Firestore回调默认在主线程执行:虽然get()和update()是异步网络操作,但它们的addOnCompleteListener默认会回到UI线程处理结果。直播1小时后,deviceInfo文档里的数组字段(connectionInfo、bandwidthAlerts等)积累了大量数据,回调中处理这些数据的过程会阻塞UI线程。
  2. 重复查询整个集合:每次调用dropDeviceInfo都要get()整个deviceInfo集合遍历找文档ID,完全没必要,重复查询会累积耗时。
  3. 多次独立更新请求:代码中多次调用reference.update(),每次都是单独的网络请求,总耗时被放大,且这些请求的回调都在主线程执行,进一步加剧卡顿。

修复步骤

1. 缓存文档ID,避免重复查询

第一次获取到deviceInfo集合里的文档ID后,把它缓存到成员变量或SharedPreferences中,后续直接使用,不用每次都查询整个集合。

2. 把Firestore操作切换到后台线程

使用addOnCompleteListener(Executor, OnCompleteListener),传入后台线程池,让回调逻辑在后台执行,不占用UI线程。

3. 合并多次更新为单次请求

把所有要更新的字段放到同一个Map里,调用一次reference.update(map),减少网络请求次数,提升效率。

4. 确保辅助方法在后台执行

检查getNetworkClass(context)方法,如果它涉及耗时操作(比如读取系统网络状态),必须放到后台线程执行,避免阻塞UI。

修改后的代码示例

private String cachedDeviceDocId; // 缓存文档ID的成员变量

public void dropDeviceInfo(String streamId, String streamState, Boolean fromForceStart, Context context, boolean isMuted, boolean isAutoFocus, boolean adaptiveBitrateEnabled, int currentResolutionTarget, StreamQuality targetStreamQuality, boolean isDropInitialBandwithAlerts, String bandwidthMessage, String alertType, boolean shouldAddConnectivityBW, boolean shouldAddDeviceDetail, boolean shouldAddAFInfo, boolean shouldAddAudioInfo, boolean shouldDropNetWorkSource) {
    FirebaseFirestore db = FirebaseFirestore.getInstance();
    Executor backgroundExecutor = Executors.newSingleThreadExecutor();

    // 优先使用缓存的文档ID,无缓存时再查询
    if (cachedDeviceDocId == null) {
        db.collection("livestreamTechnicals").document(streamId).collection("deviceInfo")
                .get()
                .addOnCompleteListener(backgroundExecutor, task -> {
                    if (task.isSuccessful() && task.getResult().size() > 0) {
                        // 假设集合中只有一个文档,直接取第一个的ID
                        cachedDeviceDocId = task.getResult().getDocuments().get(0).getId();
                        // 执行更新操作
                        executeDeviceInfoUpdate(db, streamId, cachedDeviceDocId, context, isMuted, isAutoFocus, bandwidthMessage, shouldAddAudioInfo, shouldAddAFInfo, streamState);
                    }
                });
    } else {
        executeDeviceInfoUpdate(db, streamId, cachedDeviceDocId, context, isMuted, isAutoFocus, bandwidthMessage, shouldAddAudioInfo, shouldAddAFInfo, streamState);
    }
}

// 后台执行的更新逻辑
private void executeDeviceInfoUpdate(FirebaseFirestore db, String streamId, String docId, Context context, boolean isMuted, boolean isAutoFocus, String bandwidthMessage, boolean shouldAddAudioInfo, boolean shouldAddAFInfo, String streamState) {
    DocumentReference docRef = db.collection("livestreamTechnicals").document(streamId).collection("deviceInfo").document(docId);
    Map<String, Object> updateData = new HashMap<>();

    // 准备networkSource数据
    Map<String, Object> netSource = new HashMap<>();
    netSource.put("networkSource", getNetworkClass(context)); // 确保此方法无耗时操作
    netSource.put("timestamp", new Timestamp(new Date()));
    updateData.put("connectionInfo", FieldValue.arrayUnion(netSource));

    // 准备带宽告警数据
    Map<String, Object> bwAlerts = new HashMap<>();
    bwAlerts.put("message", bandwidthMessage);
    bwAlerts.put("timestamp", new Timestamp(new Date()));
    updateData.put("bandwidthAlerts", FieldValue.arrayUnion(bwAlerts));

    // 更新流状态
    updateData.put("streamState", streamState);

    // 处理音频信息(如果需要)
    if (shouldAddAudioInfo) {
        Map<String, Object> audioDetails = new HashMap<>();
        audioDetails.put("message", isMuted ? "Audio is now turned off" : "Audio is now turned on");
        audioDetails.put("timestamp", new Timestamp(new Date()));
        updateData.put("streamAudioInfo", FieldValue.arrayUnion(audioDetails));
    }

    // 处理自动对焦信息(如果需要)
    if (shouldAddAFInfo) {
        Map<String, Object> autoFocusDetails = new HashMap<>();
        autoFocusDetails.put("message", isAutoFocus ? "Autofocus - ON" : "Autofocus - OFF");
        autoFocusDetails.put("timestamp", new Timestamp(new Date()));
        updateData.put("focusInfo", FieldValue.arrayUnion(autoFocusDetails));
    }

    // 单次更新所有字段
    docRef.update(updateData)
            .addOnSuccessListener(backgroundExecutor, aVoid -> Log.d("deviceInfo", "更新成功"))
            .addOnFailureListener(backgroundExecutor, Throwable::printStackTrace);
}

额外优化建议

Firestore单文档最大限制为1MB,每分钟往数组里添加元素,1小时后文档会越来越大,最终可能达到限制导致操作失败。建议把这些日志类数据拆分到子集合中,比如将connectionInfo做成deviceInfo文档下的子集合,每次添加一个独立文档,而非往数组中追加元素,这样既避免文档体积超限,查询和更新效率也会更高。

内容的提问来源于stack exchange,提问作者Kabilan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 15:47:01