All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.leancloud.push.lite.ws.AVConnectionManager Maven / Gradle / Ivy

The newest version!
package cn.leancloud.push.lite.ws;

import android.content.Context;
import android.util.Log;

import org.java_websocket.framing.CloseFrame;
import com.alibaba.fastjson.JSONObject;

import java.net.URI;
import java.nio.ByteBuffer;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;

import cn.leancloud.push.lite.AVCallback;
import cn.leancloud.push.lite.AVException;
import cn.leancloud.push.lite.AVInstallation;
import cn.leancloud.push.lite.AVOSCloud;
import cn.leancloud.push.lite.proto.CommandPacket;
import cn.leancloud.push.lite.proto.LoginPacket;
import cn.leancloud.push.lite.proto.Messages;
import cn.leancloud.push.lite.rest.AVHttpClient;
import cn.leancloud.push.lite.utils.PacketAssembler;
import cn.leancloud.push.lite.utils.StringUtil;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class AVConnectionManager implements WebSocketClientMonitor {
  private static final String TAG = AVConnectionManager.class.getSimpleName();

  private static AVConnectionManager instance = null;
  private AVStandardWebSocketClient webSocketClient = null;
  private Object webSocketClientWatcher = new Object();
  private String currentRTMConnectionServer = null;
  private int retryConnectionCount = 0;
  private boolean connectionEstablished = false;

  private volatile boolean connecting = false;
  private volatile AVCallback pendingCallback = null;
  private Context context;
  private Map connectionListeners = new ConcurrentHashMap<>(1);

  public synchronized static AVConnectionManager getInstance(Context context) {
    if (instance == null) {
      instance = new AVConnectionManager(context, false);
    }
    return instance;
  }

  private AVConnectionManager(Context context, boolean autoConnection) {
    this.context = context;
    connectionListeners.put(AVPushMessageListener.DEFAULT_ID, AVPushMessageListener.getInstance());
    if (autoConnection) {
      startConnection();
    }
  }

  private void resetConnectingStatus(boolean succeed) {
    this.connecting = false;
    if (null != pendingCallback) {
      if (succeed) {
        pendingCallback.internalDone(null);
      } else {
        pendingCallback.internalDone(new AVException(AVException.TIMEOUT, "network timeout."));
      }
    }
    pendingCallback = null;
  }

  private void reConnectionRTMServer() {
    retryConnectionCount++;
    if (retryConnectionCount <= 3) {
      new Thread(new Runnable() {
        @Override
        public void run() {
          try {
            long sleepMS = (long)Math.pow(2, retryConnectionCount) * 1000;
            Thread.sleep(sleepMS);
            if (AVOSCloud.isDebugLogEnabled()) {
              Log.d(TAG, "reConnect rtm server. count=" + retryConnectionCount);
            }
            startConnectionInternal();
          } catch (InterruptedException ex) {
            Log.w(TAG,"failed to start connection.", ex);
          }
        }
      }).start();
    } else {
      Log.e(TAG, "have tried " + (retryConnectionCount - 1) + " times, stop connecting...");
      resetConnectingStatus(false);
    }
  }

  private String updateTargetServer(JSONObject rtmConnectionServerResponse) {
    String primaryServer = rtmConnectionServerResponse.getString("server");
    String secondary = rtmConnectionServerResponse.getString("secondary");
    if (StringUtil.isEmpty(this.currentRTMConnectionServer) || this.currentRTMConnectionServer.equalsIgnoreCase(secondary)) {
      this.currentRTMConnectionServer = primaryServer;
    } else {
      this.currentRTMConnectionServer = secondary;
    }
    return this.currentRTMConnectionServer;
  }

  private void initWebSocketClient(String targetServer) {
    if (AVOSCloud.isDebugLogEnabled()) {
      Log.d(TAG, "try to connect server: " + targetServer);
    }

    SSLSocketFactory sf = null;
    try {
      SSLContext sslContext = SSLContext.getDefault();
      sf = sslContext.getSocketFactory();
    } catch (NoSuchAlgorithmException exception) {
      Log.e(TAG, "failed to get SSLContext, cause: " + exception.getMessage());
    }
    URI targetURI;
    try {
      targetURI = URI.create(targetServer);
    } catch (Exception ex) {
      Log.e(TAG, "failed to parse targetServer:" + targetServer + ", cause:" + ex.getMessage());
      targetURI = null;
    }
    if (null == targetURI) {
      return;
    }

    synchronized (webSocketClientWatcher) {
      if (null != webSocketClient) {
        try {
          webSocketClient.close();
        } catch (Exception ex) {
          Log.e(TAG, "failed to close websocket client.", ex);
        } finally {
          webSocketClient = null;
        }
      }
      int connectTimeout = 30000;// milliseconds
      webSocketClient = new AVStandardWebSocketClient(targetURI, AVStandardWebSocketClient.SUB_PROTOCOL_2_3,
          true, true, sf, connectTimeout, AVConnectionManager.this);
      webSocketClient.connect();
    }
  }

  public void startConnection(AVCallback callback) {
    if (this.connectionEstablished) {
      if (null != callback) {
        callback.internalDone(null);
      }
    } else if (this.connecting) {
      if (null != callback) {
        this.pendingCallback = callback;
      }
    } else {
      this.connecting = true;
      this.pendingCallback = callback;
      startConnectionInternal();
    }
  }

  public void startConnection() {
    startConnection(null);
  }

  private void startConnectionInternal() {
    final String appId = AVOSCloud.applicationId;
    final String installationId = AVInstallation.getCurrentInstallation(this.context).getInstallationId();

    AVHttpClient.getInstance().fetchPushWSServer(appId, installationId, 1, new Callback() {
      public void onResponse(Call call, Response response) {
        JSONObject result = response.body();
        if (null != result) {
          String targetServer = updateTargetServer(result);
          initWebSocketClient(targetServer);
        }
      }

      public void onFailure(Call call, Throwable t) {
        Log.w(TAG, "failed to fetch WebSocket Server.", t);
        reConnectionRTMServer();
      }
    });
  }

  public void cleanup() {
    resetConnection();

    this.connectionListeners.clear();
    this.pendingCallback = null;
  }

  void resetConnection() {
    synchronized (webSocketClientWatcher) {
      if (null != webSocketClient) {
        try {
          webSocketClient.closeConnection(CloseFrame.ABNORMAL_CLOSE, "Connectivity broken");
        } catch (Exception ex) {
          Log.e(TAG, "failed to close websocket client.", ex);
        } finally {
          webSocketClient = null;
        }
      }
    }

    connectionEstablished = false;
    retryConnectionCount = 0;
    connecting = false;
  }

  public void sendPacket(CommandPacket packet) {
    this.webSocketClient.send(packet);
  }

  public boolean isConnectionEstablished() {
    return this.connectionEstablished;
  }

  /**
   * WebSocketClientMonitor interfaces
   */
  public void onOpen() {
    if (AVOSCloud.isDebugLogEnabled()) {
      Log.d(TAG, "webSocket established...");
    }
    connectionEstablished = true;
    retryConnectionCount = 0;
    for (AVConnectionListener listener: connectionListeners.values()) {
      listener.onWebSocketOpen();
    }

    resetConnectingStatus(true);

    // auto send login packet.
    LoginPacket lp = new LoginPacket();
    lp.setAppId(AVOSCloud.getApplicationId());
    lp.setInstallationId(AVInstallation.getCurrentInstallation(this.context).getInstallationId());
    this.sendPacket(lp);
  }

  public void onClose(int var1, String var2, boolean var3) {
    if (AVOSCloud.isDebugLogEnabled()) {
      Log.d(TAG, "webSocket closed...");
    }
    connectionEstablished = false;
    for (AVConnectionListener listener: connectionListeners.values()) {
      listener.onWebSocketClose();
    }
  }

  public void onMessage(ByteBuffer bytes) {
    Messages.GenericCommand command = PacketAssembler.getInstance().disassemblePacket(bytes);
    if (null == command) {
      return;
    }

    if (AVOSCloud.isDebugLogEnabled()) {
      Log.d(TAG, "downlink: " + command.toString());
    }

    String peerId = command.getPeerId();
    if (StringUtil.isEmpty(peerId)) {
      peerId = AVPushMessageListener.DEFAULT_ID;
    }
    Integer requestKey = command.hasI() ? command.getI() : null;
    AVConnectionListener listener = this.connectionListeners.get(peerId);
    if (null != listener) {
      listener.onMessageArriving(peerId, requestKey, command);
    } else {
      Log.w(TAG, "no peer subscribed message, ignore it. peerId=" + peerId + ", requestKey=" + requestKey);
    }
  }

  public void onError(Exception exception) {
    connectionEstablished = false;
    reConnectionRTMServer();
    for (AVConnectionListener listener: connectionListeners.values()) {
      listener.onError(null, null);
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy