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

在Openfire v4.7.3的Android Smack应用中启用流管理解决消息投递问题

解决Openfire+Smack断网后消息无法补发的问题

1. 本地持久化缓存待发送消息

断网时消息无法直接投递,必须先存入本地存储,等网络恢复后再补发。用Room或SQLite实现即可:

// Room示例:待发消息实体类
@Entity(tableName = "pending_messages")
data class PendingMessage(
    @PrimaryKey(autoGenerate = true) val id: Int = 0,
    val toJid: String,
    val content: String,
    val receiptId: String, // 用于匹配送达回执(可选)
    val timestamp: Long,
    @ColumnInfo(defaultValue = "0") val isSent: Boolean
)

发送消息时先判断网络状态,无网直接存库;有网尝试发送,失败再存库:

public void sendMessage(String toJid, String content) {
    if (!isNetworkAvailable()) {
        pendingMessageDao.insert(new PendingMessage(toJid, content, UUID.randomUUID().toString(), System.currentTimeMillis(), false));
        return;
    }

    Message message = new Message(toJid, Message.Type.chat);
    message.setBody(content);
    String receiptId = UUID.randomUUID().toString();
    DeliveryReceiptManager.addDeliveryReceiptRequest(message, receiptId);

    try {
        connection.sendStanza(message);
    } catch (SmackException.NotConnectedException | InterruptedException e) {
        pendingMessageDao.insert(new PendingMessage(toJid, content, receiptId, System.currentTimeMillis(), false));
        e.printStackTrace();
    }
}

2. 监听Smack连接状态,触发重发

注册ConnectionListener,当连接建立或重新认证后,自动查询本地待发消息并重试:

connection.addConnectionListener(new ConnectionListener() {
    @Override
    public void connected(XMPPConnection connection) {
        retryPendingMessages();
    }

    @Override
    public void authenticated(XMPPConnection connection, boolean resumed) {
        retryPendingMessages();
    }

    // 忽略其他未用到的方法
});

private void retryPendingMessages() {
    List<PendingMessage> unsentMessages = pendingMessageDao.getUnsentMessages();
    for (PendingMessage msg : unsentMessages) {
        Message message = new Message(msg.getToJid(), Message.Type.chat);
        message.setBody(msg.getContent());
        DeliveryReceiptManager.addDeliveryReceiptRequest(message, msg.getReceiptId());
        
        try {
            connection.sendStanza(message);
            pendingMessageDao.markAsSent(msg.getId());
        } catch (SmackException.NotConnectedException | InterruptedException e) {
            // 发送失败,保留消息等待下次重试
            e.printStackTrace();
        }
    }
}

3. 配合Android网络状态监听

除了Smack的连接监听,还可以用Android系统的网络回调,确保网络恢复时及时触发重发:

private void registerNetworkCallback() {
    ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkRequest request = new NetworkRequest.Builder()
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .build();

    cm.registerNetworkCallback(request, new ConnectivityManager.NetworkCallback() {
        @Override
        public void onAvailable(Network network) {
            super.onAvailable(network);
            if (connection != null && connection.isConnected()) {
                retryPendingMessages();
            }
        }
    });
}

4. 启用消息送达回执(可选)

Openfire支持XEP-0184回执标准,开启后可以确认消息是否真的到达服务器,避免假发送:

// 初始化回执管理器
DeliveryReceiptManager receiptManager = DeliveryReceiptManager.getInstanceFor(connection);
receiptManager.setAutoReceiptMode(DeliveryReceiptManager.AutoReceiptMode.always);
receiptManager.addReceiptReceivedListener((fromJid, receiptId, message) -> {
    // 收到回执,标记消息为已送达
    pendingMessageDao.markAsDelivered(receiptId);
});

注意事项

  • 重发时可设置重试次数上限,避免无限重试无效消息
  • 处理并发问题,比如多线程触发重发时,用数据库事务保证状态一致性
  • 可以定期清理已发送/超时的待发消息,避免本地存储冗余

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 13:07:32