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

com.avos.avoscloud.AVPushConnectionManager Maven / Gradle / Ivy

The newest version!
package com.avos.avoscloud;

import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;

import com.avos.avoscloud.im.v2.AVIMClient;
import org.java_websocket.framing.CloseFrame;
import com.avos.avospush.session.LiveQueryLoginPacket;
import com.avos.avospush.push.AVPushRouter;
import com.avos.avospush.session.CommandPacket;
import com.avos.avospush.session.LoginPacket;
import com.avos.avospush.session.MessagePatchModifiedPacket;
import com.avos.avospush.session.PushAckPacket;
import com.google.protobuf.InvalidProtocolBufferException;

import java.lang.reflect.Method;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * Created by yangchaozhong on 3/14/14.
 */
class AVPushConnectionManager implements AVPushWebSocketClient.AVPacketParser {
  private static final String SUB_PROTOCOL_2_3 = "lc.protobuf2.3";
  private static final String SUB_PROTOCOL_2_1 = "lc.protobuf2.1";
  private static AVPushConnectionManager instance = null;

  private volatile AVPushWebSocketClient socketClient = null;
  private Object socketClientObject = new Object();
  private AVPushRouter.RouterResponseListener routerResponseListener = null;

  private static final Map peerIdEnabledSessions = Collections
      .synchronizedMap(new HashMap());

  private List connectionCallbacks = new LinkedList();
  private AVPushRouter router;

  private static String liveQuerySubscribeId = "";

  private AVPushConnectionManager(Context ctx) {
    LogUtil.log.d("begin to invoke AVPushConnectionManager(Context)");
    routerResponseListener = new AVPushRouter.RouterResponseListener() {
      @Override
      public void onServerAddress(String address) {
        try {
          if (!AVUtils.isBlankString(address)) {
            createNewWebSocket(address);
          } else {
            if (AVOSCloud.isDebugLogEnabled()) {
              LogUtil.avlog.d("push server address is null");
            }
          }
        } catch (Exception ex) {
          LogUtil.avlog.e("failed to create new ws. cause: " + ex.getMessage());
        }
      }
    };
    router = new AVPushRouter(ctx, routerResponseListener);

    initSessionsIfExists();
    boolean initializeConn = true;
    if (AVOSCloud.isGcmOpen()) {
      try {
        Class gcmManagerClass = Class.forName("com.avos.avoscloud.AVGcmManager");
        Method getTokenMethod = gcmManagerClass.getMethod("getGcmTokenInBackground", Context.class);
        getTokenMethod.invoke(gcmManagerClass, ctx);
        initializeConn = false;
        LogUtil.log.d("skip initialize connection bcz of GCM Push using");
      } catch (Exception e) {
        if (AVOSCloud.isDebugLogEnabled()) {
          LogUtil.avlog.i("gcm library not started since not included");
        }
      }
    } else if (AVOSCloud.isFCMOpen()) {
      initializeConn = false;
      LogUtil.log.d("skip initialize connection bcz of FCM Push using");
    }
    if (initializeConn) {
      initConnection();
    }
    LogUtil.log.d("end of AVPushConnectionManager(Context)");
  }

  private void initSessionsIfExists() {
    Map cachedSessions = AVSessionCacheHelper.getTagCacheInstance().getAllSession();
    for (Map.Entry entry : cachedSessions.entrySet()) {
      AVSession s = this.getOrCreateSession(entry.getKey());
      s.sessionResume.set(true);
      s.tag = entry.getValue();
    }
    if (AVOSCloud.isDebugLogEnabled()) {
      LogUtil.avlog.d(cachedSessions.size() + " sessions recovered");
    }
  }

  public synchronized static AVPushConnectionManager getInstance(Context ctx) {
    if (instance == null) {
      instance = new AVPushConnectionManager(ctx);
    }

    return instance;
  }

  public void initConnection() {
    this.initConnection(null);
  }

  public void initConnection(final AVCallback cl) {
    synchronized (socketClientObject) {
      if (socketClient != null && socketClient.isOpen()) {
        LogUtil.log.d("push connection is open");
        return;
      } else if (socketClient != null) {
        socketClient.cancelReconnect();
      }
    }

    LogUtil.log.d("try to query connection server via push router.");
    router.fetchPushServer();
    if (null != cl) {
      connectionCallbacks.add(cl);
    }
  }

  public boolean isConnectedToPushServer() {
    return (socketClient != null && socketClient.isOpen());
  }

  public void stop() {
    cleanupSocketConnection();
  }

  public AVSession getOrCreateSession(String peerId) {
    try {
      // 据说这行有NPE,所以不得不catch起来避免app崩溃
      boolean newAdded = !peerIdEnabledSessions.containsKey(peerId);
      AVSession session = null;
      if (newAdded) {
        session = new AVSession(peerId, new AVDefaultSessionListener(this));
        peerIdEnabledSessions.put(peerId, session);
        session.getWebSocketListener().onListenerAdded(
            (this.socketClient != null && socketClient.isOpen()));
      } else {
        session = peerIdEnabledSessions.get(peerId);
      }
      return session;
    } catch (Exception e) {
      return null;
    }
  }

  public void removeSession(String peerId) {
    AVSession session = peerIdEnabledSessions.remove(peerId);
    if (session != null && session.getWebSocketListener() != null) {
      session.getWebSocketListener().onListenerRemoved();
    }
  }

  public void sendData(final CommandPacket packet) {
    if (socketClient != null && socketClient.isOpen()) {
      socketClient.send(packet);
    }
  }

  public void cleanupSocketConnection() {
    this.cleanupSocketConnection(CloseFrame.NORMAL, "");
  }

  public void cleanupSocketConnection(final int code, final String message) {
    synchronized (socketClientObject) {
      if (socketClient != null && (socketClient.isConnecting() || socketClient.isOpen())) {
        try {
          LogUtil.avlog.d("try to close and destroy connection");
          socketClient.closeConnection(code, message);
          socketClient.destroy();
        } catch (Exception e) {
          if (AVOSCloud.isDebugLogEnabled()) {
            LogUtil.avlog.e("Close socket client failed.", e);
          }
        }
      } else if (socketClient != null && socketClient.isClosing()) {
        LogUtil.avlog.d("try to destroy connection");
        socketClient.destroy();
        socketClient = null;
      } else {
        LogUtil.avlog.d("do nothing for invalid connection");
      }
    }
  }

  private void createNewWebSocket(final String pushServer) {
    synchronized(socketClientObject) {
      if (socketClient == null || socketClient.isDestroyed() || socketClient.isClosed()) {
        // 由于需要链接到新的server address上,原来的client就要被抛弃了,抛弃前需要取消自动重连的任务
        if (socketClient != null && !socketClient.isDestroyed()) {
          LogUtil.log.d("destroy socketClient first which is closed.");
          socketClient.destroy();
        }

        if (AVSession.isOnlyPushCount()) {
          socketClient =
              new AVPushWebSocketClient(URI.create(pushServer), this, SUB_PROTOCOL_2_3, true);
        } else {
          socketClient =
              new AVPushWebSocketClient(URI.create(pushServer), this, SUB_PROTOCOL_2_1, true);
        }

        socketClient.connect();
        if (AVOSCloud.isDebugLogEnabled()) {
          LogUtil.avlog.d("connect to server: " + pushServer);
        }
      } else {
        LogUtil.log.d("skip create socketClient.");
      }
    }
  }


  private void processLiveQueryData(Messages.DataCommand dataCommand) {
    List messageIds = dataCommand.getIdsList();
    List messages = dataCommand.getMsgList();

    ArrayList dataList = new ArrayList<>();
    for (int i = 0; i < messages.size() && i < messageIds.size(); i++) {
      Messages.JsonObjectMessage message = messages.get(i);
      if (null != message) {
        dataList.add(message.getData());
      }
    }
    AVLiveQuery.processData(dataList);
  }

  private void processDataCommand(Messages.DataCommand dataCommand) {
    List messageIds = dataCommand.getIdsList();
    List messages = dataCommand.getMsgList();
    for (int i = 0; i < messages.size() && i < messageIds.size(); i++) {
      if (null != messages.get(i)) {
        AVNotificationManager.getInstance().processPushMessage(messages.get(i).getData(), messageIds.get(i));
      }
    }
    PushAckPacket pap = new PushAckPacket();
    pap.setInstallationId(AVInstallation.getCurrentInstallation().getInstallationId());
    pap.setMessageIds(messageIds);
    sendData(pap);
  }

  public void processCommand(ByteBuffer bytes) {
    try {
      Messages.GenericCommand command = Messages.GenericCommand.parseFrom(bytes.array());
      if (AVOSCloud.isDebugLogEnabled()) {
        LogUtil.avlog.d("downlink : " + command.toString());
      }

      String peerId = command.getPeerId();
      Integer requestKey = command.hasI() ? command.getI() : null;
      if (AVUtils.isBlankString(peerId)) {
        // in case that only 1 client loggined, downlink doesn't contains peerId.
        peerId = AVIMClient.getDefaultClient();
      }

      if (command.getCmd().getNumber() == Messages.CommandType.loggedin_VALUE) {
        if (LiveQueryLoginPacket.SERVICE_LIVE_QUERY == command.getService()) {
          processLoggedinCommand(requestKey);
        }
      } else if (!peerIdEnabledSessions.isEmpty()
          || command.getCmd().getNumber() == Messages.CommandType.data_VALUE) {
        switch (command.getCmd().getNumber()) {
          case Messages.CommandType.data_VALUE:
            if (command.hasService()) {
              final int service = command.getService();
              if (LiveQueryLoginPacket.SERVICE_PUSH == service) {
                processDataCommand(command.getDataMessage());
              } else if (LiveQueryLoginPacket.SERVICE_LIVE_QUERY == service) {
                processLiveQueryData(command.getDataMessage());
              }
            } else {
              processDataCommand(command.getDataMessage());
            }
            break;
          case Messages.CommandType.direct_VALUE:
            processDirectCommand(peerId, command.getDirectMessage());
            break;
          case Messages.CommandType.session_VALUE:
            processSessionCommand(peerId, command.getOp().name(), requestKey,
                command.getSessionMessage());
            break;
          case Messages.CommandType.ack_VALUE:
            processAckCommand(peerId, requestKey, command.getAckMessage());
            break;
          case Messages.CommandType.rcp_VALUE:
            processRpcCommand(peerId, command.getRcpMessage());
            break;
          case Messages.CommandType.conv_VALUE:
            processConvCommand(peerId, command.getOp().name(), requestKey,
                command.getConvMessage());
            break;
          case Messages.CommandType.error_VALUE:
            processErrorCommand(peerId, requestKey, command.getErrorMessage());
            break;
          case Messages.CommandType.logs_VALUE:
            processLogsCommand(peerId, requestKey, command.getLogsMessage());
            break;
          case Messages.CommandType.unread_VALUE:
            processUnreadCommand(peerId, command.getUnreadMessage());
            break;
          case Messages.CommandType.blacklist_VALUE:
            processBlacklistCommand(peerId, command.getOp().name(), requestKey, command.getBlacklistMessage());
            break;
          case Messages.CommandType.patch_VALUE:
            if(command.getOp().equals(Messages.OpType.modify)) {
              // modify 为服务器端主动推送的 patch 消息
              processPatchCommand(peerId, true, requestKey, command.getPatchMessage());
            } else if (command.getOp().equals(Messages.OpType.modified)) {
              // modified 代表的是服务器端对于客户端请求的相应
              processPatchCommand(peerId, false, requestKey, command.getPatchMessage());
            }
            break;
          case Messages.CommandType.goaway_VALUE:
            processGoawayCommand(peerId);
            break;
          default:
            break;
        }
      }
    } catch (InvalidProtocolBufferException e) {
      e.printStackTrace();
    }
  }

  /**
   * 清空 connection list,因为会有在重连时发送多个请求的情况,所以需要一个 list 来存储
   * 主要逻辑运行在主线程
   */
  @Override
  public synchronized void processConnectionStatus(final AVException e) {
    router.processSocketConnectionResult(e);
    AVOSCloud.handler.post(new Runnable() {
      @Override
      public void run() {
        if (null != connectionCallbacks && connectionCallbacks.size() > 0) {
          Iterator iterator = connectionCallbacks.iterator();
          while (iterator.hasNext()) {
            AVCallback callback = iterator.next();
            if (null != callback) {
              callback.internalDone(null, e);
            }
            iterator.remove();
          }
        }
      }
    });
  }

  private void processSessionCommand(String peerId, String op, Integer requestId,
                                     Messages.SessionCommand command) {
    AVSession session = peerIdEnabledSessions.get(peerId);
    if (session != null && session.getWebSocketListener() != null) {
      session.getWebSocketListener().onSessionCommand(op, requestId, command);
    }
  }

  private void processDirectCommand(String peerId, Messages.DirectCommand directCommand) {
    AVSession session = peerIdEnabledSessions.get(peerId);
    if (session != null && session.getWebSocketListener() != null) {
      session.getWebSocketListener().onDirectCommand(directCommand);
    }
  }

  private void processRpcCommand(String peerId, Messages.RcpCommand command) {
    AVSession session = peerIdEnabledSessions.get(peerId);
    if (session != null && session.getWebSocketListener() != null) {
      if (command.hasRead()) {
        session.getWebSocketListener().onReadCmdReceipt(command);
      } else {
        session.getWebSocketListener().onMessageReceipt(command);
      }
    }
  }

  private void processAckCommand(String peerId, Integer requestKey, Messages.AckCommand command) {
    AVSession session = peerIdEnabledSessions.get(peerId);
    if (session != null && session.getWebSocketListener() != null) {
      session.getWebSocketListener().onAckCommand(requestKey, command);
    }
  }

  private void processBlacklistCommand(String peerId, String operation, Integer requestKey,
                                       Messages.BlacklistCommand blacklistCommand) {
    AVSession session = peerIdEnabledSessions.get(peerId);
    if (session != null && session.getWebSocketListener() != null) {
      session.getWebSocketListener().onBlacklistCommand(operation, requestKey, blacklistCommand);
    }
  }

  private void processConvCommand(String peerId, String operation, Integer requestKey,
                                  Messages.ConvCommand convCommand) {
    AVSession session = peerIdEnabledSessions.get(peerId);
    if (session != null && session.getWebSocketListener() != null) {
      session.getWebSocketListener().onConversationCommand(operation, requestKey, convCommand);
    }
  }

  private void processErrorCommand(String peerId, Integer requestKey,
                                   Messages.ErrorCommand errorCommand) {
    AVSession session = peerIdEnabledSessions.get(peerId);
    if (session != null && session.getWebSocketListener() != null) {
      session.getWebSocketListener().onError(requestKey, errorCommand);
    }
  }

  private void processLogsCommand(String peerId, Integer requestKey,
                                  Messages.LogsCommand logsCommand) {
    AVSession session = peerIdEnabledSessions.get(peerId);
    session.getWebSocketListener().onHistoryMessageQuery(requestKey, logsCommand);
  }

  private void processUnreadCommand(String peerId, Messages.UnreadCommand unreadCommand) {
    AVSession session = peerIdEnabledSessions.get(peerId);
    if (session != null && session.getWebSocketListener() != null) {
      session.getWebSocketListener().onUnreadMessagesCommand(unreadCommand);
    }
  }

  private void processPatchCommand(String peerId, boolean isModify, Integer requestKey, Messages.PatchCommand patchCommand) {
    AVSession session = peerIdEnabledSessions.get(peerId);
    if (null != session && null != session.getWebSocketListener()) {
      session.getWebSocketListener().onMessagePatchCommand(isModify, requestKey, patchCommand);
    }

    if (isModify) {
      long lastPatchTime = 0;
      for (Messages.PatchItem item : patchCommand.getPatchesList()) {
        if (item.getPatchTimestamp() > lastPatchTime) {
          lastPatchTime = item.getPatchTimestamp();
        }
      }
      sendData(MessagePatchModifiedPacket.getPatchMessagePacket(peerId, lastPatchTime));
    }
  }

  private void processLoggedinCommand(Integer requestKey) {
    if (null != requestKey) {
      Intent intent = new Intent();
      intent.setAction(AVLiveQuery.LIVEQUERY_PRIFIX + requestKey);
      LocalBroadcastManager.getInstance(AVOSCloud.applicationContext).sendBroadcast(intent);
    }
  }

  private void processGoawayCommand(String peerId) {
    AVSession session = peerIdEnabledSessions.get(peerId);
    if (session != null && session.getWebSocketListener() != null) {
      session.getWebSocketListener().onGoaway();
    } else {
      LogUtil.avlog.d("peerId(" + peerId + ") is invalid within GOAWAY command.");
    }
  }

  public void sendLiveQueryLoginCmd(String subscribeId, int requestId) {
    if (!AVUtils.isBlankString(subscribeId)) {
      liveQuerySubscribeId = subscribeId;
      LiveQueryLoginPacket lp = new LiveQueryLoginPacket();
      lp.setSubscribeId(subscribeId);
      if (0 != requestId) {
        lp.setRequestId(requestId);
      }
      socketClient.send(lp);
    }
  }

  @Override
  public void loginCmd() {
    LoginPacket lp = new LoginPacket();
    lp.setAppId(AVOSCloud.applicationId);
    lp.setInstallationId(AVInstallation.getCurrentInstallation().getInstallationId());
    socketClient.send(lp);

    if (!AVUtils.isBlankString(liveQuerySubscribeId)) {
      sendLiveQueryLoginCmd(liveQuerySubscribeId, 0);
    }
  }

  @Override
  public void processSessionsStatus(boolean closeEvent) {
    for (AVSession session : peerIdEnabledSessions.values()) {
      if (session.getWebSocketListener() != null) {
        if (closeEvent) {
          session.getWebSocketListener().onWebSocketClose();
        } else {
          session.getWebSocketListener().onWebSocketOpen();
        }
      }
    }
  }


  @Override
  public void processRemoteServerNotAvailable() {
    router.fetchPushServer();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy