如何使用(a)smack在Android上保持XMPP连接稳定?

27
我使用 asmack-android-7-beem 库来开发 Android 应用,并有一个后台服务运行以保持应用程序的活力。但是不久之后,XMPP 连接会无任何通知地断开。服务器显示客户端仍然在线,但没有发送或接收数据包。
例如,当其他客户端有新的出席信息时,该客户端不会收到任何出席信息数据包。我将 XMPPConnection 设置为我的主要 Application 类的属性。
在建立连接之前,我设置了 ConnectionConfiguration config.setReconnectionAllowed(true)
但是重连并未发生。 XMPPConnection connection.isConnected() 返回 true。
因此,客户端不知道连接已实际丢失。是否有任何方法可以保持连接活动?

2
相关链接:https://dev59.com/b2XWa4cB1Zd3GeqPP8La - Flow
isConnected方法返回对象状态,但Smack无法处理网络连接更改,您需要编写代码来检查您是否能够ping服务器,根据这个结果执行进一步的操作。有关更多详细信息,请参见我的答案。 - Mandeep
6个回答

12
在使用 asmack 时,请在应用程序中添加以下代码,以使 Dalvik 加载 ReconnectionManager 类并运行其静态初始化块:
static {
    try {
        Class.forName("org.jivesoftware.smack.ReconnectionManager");
    } catch (ClassNotFoundException ex) {
        // problem loading reconnection manager
    }
}

2
需要解释一下吗? - ingyesid
2
@ingyesid 这是执行 ReconnectionManager 类中静态初始化块所需的,该块实际上会激活它。但我建议再次检查,因为如果您使用 asmack 的话,它应该由 asmack 静态初始化器进行初始化。 - dant3
嗨,@Martin Konecny。我一直在尝试创建一个XMPPConnection,但是连接失败了。不过,XMPPServer启动正常。你能否请看一下我的问题这里,看看是否可以帮忙?非常感谢您的帮助。 - Program-Me-Rev
3
在Android上使用ReconnectionManager通常不是完整的解决方案,因为您希望在数据连接发生更改时(例如GSM到Wifi切换)做出反应,而ReconnectionManager不知道例如Android的NETWORK_CONNECTIVITY_CHANGED意图。此外,在较新的Smack版本(4.1或更高版本)上(运行于Android上),不再需要手动进行初始化:Smack将自动完全初始化。 - Flow

8

实际上,重连管理器没有任何问题。首先,您需要将连接监听器添加到您的连接管理器中。

connection.addConnectionListener(new ConnectionListener() {

                    @Override
                    public void reconnectionSuccessful() {
                        Log.i("","Successfully reconnected to the XMPP server.");

                    }

                    @Override
                    public void reconnectionFailed(Exception arg0) {
                        Log.i("","Failed to reconnect to the XMPP server.");
                    }

                    @Override
                    public void reconnectingIn(int seconds) {
                        Log.i("","Reconnecting in " + seconds + " seconds.");
                    }

                    @Override
                    public void connectionClosedOnError(Exception arg0) {
                        Log.i("","Connection to XMPP server was lost.");
                    }

                    @Override
                    public void connectionClosed() {
                        Log.i("","XMPP connection was closed.");

                    }
                }); 

如果发生任何错误,当连接关闭时,将自动调用connectionClosedOnError(Exception arg0)。

 public void connectionClosed() {
                        Log.i("","XMPP connection was closed.");
                        //You can manually call reconnection code if you                  want to reconnect on any connection close
                    }

然后检查它,这将调用reconnectingin()方法并尝试重新连接。

希望这可以帮助您。

使用以下代码来检查连接 PingManager pingManager = PingManager.getInstanceFor(connection); pingManager.setPingInterval(5000);

添加ping失败处理的监听器以处理连接是否已连接,因为isConnected方法不可靠地检查连接状态。

pingManager.registerPingFailedListener(PingFailedListener);

对于移动网络连接性是非常大的问题,因此您需要使用广播接收器检查移动设备的网络连接性,并在数据重新连接时使用pingMyServer方法来检查连接是否存活。如果您从服务器获得ping回复,则表示连接存活,否则在ping失败时您可以手动重新连接连接。


嗨 Mandeep,我完全按照你的答案进行了操作,但仍然无法正常工作。连接只能维持大约 5 分钟。我认为我可能漏掉了什么。这是我的代码:链接 MainActivity.java 你可以看一下并告诉我是否和你的一样吗?谢谢 https://www.dropbox.com/s/ze3vjy08m2mymir/MainActivity.java?dl=0 我使用的是 Android API 16 和 aSmack: asmack-android-19-0.8.10.jar。 - Hoa Vu
我有同样的问题,使用相同的 .jar 文件,你找出为什么连接会在一段时间后丢失了吗? - Silvia H
嗨,@Silvia.H。我一直在尝试创建一个XMPPConnection,但是连接失败了。不过,XMPPServer启动正常。你能否请看一下我的问题这里,看看是否可以帮忙?非常感谢。 - Program-Me-Rev

3

以下是适用于ReconnectionManager的代码,它可以正常工作:

1)在xmpp连接上添加addConnectionListener

XMPPConnectionListener mConnectionListener = new XMPPConnectionListener(username);
connection.addConnectionListener(mConnectionListener);

2)如果连接关闭,可以使用ReconnectionManager类自动重新连接。

 ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connection);
 reconnectionManager.enableAutomaticReconnection();
 reconnectionManager.setEnabledPerDefault(true);

3) 用于重新连接、连接和经过身份验证的服务器上的ConnectionListener。如果连接成功地与服务器进行了身份验证,则还会注册PingManagerServerPingWithAlarmManager类。

public class XMPPConnectionListener implements ConnectionListener {
    String username="";
    public XMPPConnectionListener(String username){
        this.username=username;
    }
    @Override
    public void connected(final XMPPConnection connectionObeject) {

        sendPresenceAvailable();
        Log.d(TAG, "xmpp Connected()");
        connected = true;

    }

    @Override
    public void connectionClosed() {

        Log.d(TAG, "xmpp ConnectionCLosed()");

        isAuthenticatedPreviouly=false;
        connected = false;

        loggedin = false;

    }

    @Override
    public void connectionClosedOnError(Exception arg0) {
        Log.d(TAG, "xmpp ConnectionClosedOnError() :"+System.currentTimeMillis());

        isAuthenticatedPreviouly=false;
        connected = false;

        loggedin = false;
    }

    @Override
    public void reconnectingIn(int arg0) {
        Log.d(TAG, "xmpp reconnectingIn() :"+System.currentTimeMillis());

        loggedin = false;
    }

    @Override
    public void reconnectionFailed(Exception arg0) {
        Log.d(TAG, "xmpp ReconnectionFailed!");
        connected = false;

       // chat_created = false;
        loggedin = false;
        try {
            connection.connect();
        } catch (SmackException | IOException | XMPPException | InterruptedException exception) {
            exception.printStackTrace();
        }
    }

    @Override
    public void reconnectionSuccessful() {
        Log.d(TAG, "xmpp ReconnectionSuccessful");
        connected = true;
        sendPresenceAvailable();
        loggedin = false;
    }

    @Override
    public void authenticated(XMPPConnection connection2, boolean resumed) {

        Log.d(TAG, "xmpp Type Main Authenticated() :" + connection.isAuthenticated());

        if(connection.isAuthenticated()) {

            ServerPingWithAlarmManager.getInstanceFor(connection).setEnabled(true);

            PingManager pingManager = PingManager.getInstanceFor(connection);
            pingManager.setPingInterval(10);

            try {
                pingManager.pingMyServer();
                pingManager.pingMyServer(true,10);
                pingManager.pingServerIfNecessary();
                pingManager.registerPingFailedListener(new PingFailedListener() {
                    @Override
                    public void pingFailed() {
                        Log.d("Ping","pingFailed");
                        disconnect();
                        connect();
                    }
                });

            registerAllListener();
     }
}

2

我有同样的问题,不过我的程序在服务器端JVM上运行。
我一开始使用了Smack 4.0。然后我升级到了Smack 4.1,但问题仍然存在。最终我找到了一个配置模块:PingManager
使用这个之后,这种情况的发生率就下降了。

    connection = new XMPPTCPConnection(config);     
    PingManager pingManager = PingManager.getInstanceFor(connection);
    pingManager.setPingInterval(300); // seconds

1
嗨@crazytomcat,如果您正在编写Android代码,需要处理网络连接,并添加处理pingfailedlistener的代码,其中您需要编写重新连接机制。 - Mandeep
当应用程序在后台时,我们如何停止它? - Vishal Patoliya ツ

1
在Smack 4.1中,我使用ServerPingWithAlarmManager。您可以在ramzandroid博客这里找到有关保持连接活动的更多详细信息。

我无法解决这个类。我正在使用compile 'org.igniterealtime.smack:smack-extensions:4.2.0'compile 'org.igniterealtime.smack:smack-android:4.2.0' - joao2fast4u
1
@joao2fast4u:我认为它应该在smack-android-extensions上,基于这个github链接:https://github.com/igniterealtime/Smack/blob/master/smack-android-extensions/src/main/java/org/jivesoftware/smackx/ping/android/ServerPingWithAlarmManager.java - taynguyen

0
对于这些情况,您需要手动处理断开连接。也就是说,您应该拦截任何断开连接,并在连接侦听器通知您出现断开连接时进行处理。 public void connectionClosedOnError(Exception exception)
    import android.util.Log;

    import com.dagm8.core.protocols.ConnectionState;
    import com.dagm8.core.service.XMPPService;
    import com.dagm8.events.ConnectionStateEvent;

    import org.greenrobot.eventbus.EventBus;
    import org.jivesoftware.smack.ConnectionListener;
    import org.jivesoftware.smack.SmackException;
    import org.jivesoftware.smack.XMPPConnection;
    import org.jivesoftware.smack.XMPPException;
    import org.jivesoftware.smack.tcp.XMPPTCPConnection;

    import java.io.IOException;

    import static com.dagm8.core.protocols.ConnectionState.CONNECTED;
    import static com.dagm8.core.protocols.ConnectionState.DISCONNECTED;
    import static com.dagm8.core.protocols.ConnectionState.RECONNECTING;

    /**
     * dagm8-android
     * Created by Bedoy on 8/28/17.
     */

    public class ConnectionController implements ConnectionListener {

        private String TAG = getClass().getCanonicalName();

        private XMPPTCPConnection mConnection;

        public void setConnection(XMPPTCPConnection connection) {
            mConnection = connection;
        }

        public void init(XMPPTCPConnection connection) throws InterruptedException, XMPPException, SmackException, IOException {

            setConnection(connection);

            mConnection.setPacketReplyTimeout(10000);
            mConnection.addConnectionListener(this);
            mConnection.connect();
        }

        @Override
        public void connected(XMPPConnection connection) {
            XMPPService.connectionState = RECONNECTING;

            notifyConnectionState(RECONNECTING);

            try {
                mConnection.login();
            } catch (XMPPException | SmackException | IOException | InterruptedException e) {
                e.printStackTrace();
            }

            Log.i(TAG, "connected()");
        }


        @Override
        public void authenticated(XMPPConnection connection, boolean resumed) {
            XMPPService.connectionState = CONNECTED;

            notifyConnectionState(CONNECTED);

            Log.i(TAG, "authenticated()");
        }

        @Override
        public void connectionClosed() {
            XMPPService.connectionState = DISCONNECTED;

            notifyConnectionState(DISCONNECTED);

            Log.i(TAG, "connectionClosed()");
        }

        @Override
        public void connectionClosedOnError(Exception e) {
            XMPPService.connectionState = DISCONNECTED;

            notifyConnectionState(DISCONNECTED);


            try {
                mConnection.connect();
            } catch (SmackException | IOException | XMPPException | InterruptedException exception) {
                exception.printStackTrace();
            }
            Log.i(TAG, "connectionClosedOnError()");
        }

        @Override
        public void reconnectingIn(int seconds) {
            XMPPService.connectionState = RECONNECTING;

            notifyConnectionState(RECONNECTING);

            Log.i(TAG, "reconnectingIn()");
        }

        @Override
        public void reconnectionSuccessful() {
            XMPPService.connectionState = CONNECTED;

            notifyConnectionState(CONNECTED);

            Log.i(TAG, "reconnectionSuccessful()");
        }

        @Override
        public void reconnectionFailed(Exception e) {
            XMPPService.connectionState = DISCONNECTED;

            notifyConnectionState(DISCONNECTED);

            Log.i(TAG, "reconnectionFailed()");
        }


        private void notifyConnectionState(ConnectionState state) {
            EventBus.getDefault().post((ConnectionStateEvent) () -> state);
        }

        public boolean isAuthenticated()
        {
            return mConnection.isAuthenticated();
        }

        public void login() {
            try {
                mConnection.login();
            } catch (XMPPException | SmackException | IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接