登录登出

云信中登录分为手动登录和自动登录两种模式,他们的区别主要在于

1. SDK 是否会接管登录失败后的处理

2. 服务器是否会验证当前登录设备的安全性

第一点,SDK 认为手动登录即是终端用户发起登录的流程,那么在登录失败后(如网络情况不佳,密码错误)SDK 会触发相应回调,并停止重连操作,等待用户再次发起登录操作。而自动登录则会在登录失败后仍旧尝试重连,直到登录成功为止(密码错误等情况除外)。

第二点,服务器为了保障当前用户的安全性,在登录时会根据当前登录是否是自动登录进行检查设备唯一性的检查,如果当前登录为自动登录,且当前登录设备不是上一次登录设备,则会自动禁止其登录,以保证安全性。

具体说明可阅读 帐号集成与登录

手动登录

一般 APP 在首次登录、切换帐号登录、注销重登时需要手动登录,开发者需要调用 AuthService 提供的 login 接口主动发起登录请求。该接口返回类型为 AbortableFuture,允许用户在后面取消登录操作。如果服务器一直没有响应,30 秒后 RequestCallback 的 onFailed 会被调用,参数为 408 (网络连接超时)。

/**
 * 登录接口。sdk会自动连接服务器,传递用户信息,返回登录结果。<br>
 * 该操作中途可取消。如果因为网络比较差,或其他原因导致服务器迟迟没有返回,用户也没有主动取消,
 * 在45秒后AbortableFuture的onFailed会被调用到。
 *
 * @param info 登录的用户信息
 * @return AbortableFuture
 */
public AbortableFuture<LoginInfo> login(LoginInfo info);
LoginInfo 参数 说明
account 用户帐号
token 登录 token
appKey(可选) 当前应用的 appKey,一个 appKey 对应一个账号体系。
如果不填,则优先使用 SDKOptions 中配置的 appKey,
如果没有则使用 AndroidManifest 中配置的appKey
public class LoginActivity extends Activity {
    public void doLogin() {
        LoginInfo info = new LoginInfo(); // config...
        RequestCallback<LoginInfo> callback =
            new RequestCallback<LoginInfo>() {
            // 可以在此保存LoginInfo到本地,下次启动APP做自动登录用
        };
        NIMClient.getService(AuthService.class).login(info)
                .setCallback(callback);
    }
}

说明: 登录成功后,可以将用户登录信息 LoginInfo 信息保存到本地,下次启动 App 时,读取本地保存的 LoginInfo 进行自动登录。

自动登录

如果上次登录已经存在用户登录信息,那么在初始化 SDK 时传入 LoginInfo,SDK 后台会自动登录,并在登录发起前即打开相关账号的数据库,供上层调用。开发者此时无需再手动调用登录接口,可以跳过登录界面直接进入主界面。

进入主界面后,可以通过监听用户在线状态(每次注册用户在线状态监听都会立即回调通知当前的用户在线状态),或者主动获取当前用户在线状态,来判断自动登录是否成功。

// 在初始化SDK的时候,传入 loginInfo(), 其中包含用户信息,用以自动登录
NIMClient.init(this, loginInfo(), options());
public class NimApplication extends Application {

    public void onCreate() {
        // ... your codes

        // SDK初始化(启动后台服务,若已经存在用户登录信息,SDK 将完成自动登录)
        NIMClient.init(this, loginInfo(), options());

        // ... your codes
    }

    private LoginInfo loginInfo() {
        // 从本地读取上次登录成功时保存的用户登录信息
        String account = Preferences.getUserAccount();
        String token = Preferences.getUserToken();

        if (!TextUtils.isEmpty(account) && !TextUtils.isEmpty(token)) {
            DemoCache.setAccount(account.toLowerCase());
            return new LoginInfo(account, token);
        } else {
            return null;
        }
    }
}

说明:在自动登录过程中,如果没有网络或者网络断开或者与网易云通信服务器建立连接失败,会上报在线状态 NET_BROKEN,表示当前网络不可用,当网络恢复的时候,会触发断网自动重连;如果连接建立成功但登录超时,会上报在线状态 UNLOGIN,并触发自动重连,无需上层手动调用登录接口。

特别提醒: 在自动登录成功前,调用服务器相关请求接口(由于与网易云通信服务器连接尚未建立成功,会导致发包超时)会报408错误。但可以调用本地数据库相关接口获取本地数据(自动登录的情况下会自动打开相关账号的数据库)。自动登录过程中也会有用户在线状态回调。

登出

如果用户手动登出,不再接收消息和提醒,开发者可以调用 logout 方法,该方法没有回调。

/**
 * 注销接口
 */
public void logout();
NIMClient.getService(AuthService.class).logout();

注意: 登出操作,不要放在 Activity(Fragment) 的 onDestroy 方法中。

特殊错误码介绍

错误码 说明 在线状态
302 token 错误或者账号不存在都会导致 302 错误码。
这种情况一般发生于用户在其他设备上修改了密码。
PWD_ERROR
408 1、连接建立成功,SDK 发出登录请求后网易云通信服务器一直
没有响应,那么 30s 后将导致登录超时。
2、登录成功之前,调用服务器相关请求接口
(由于与网易云通信服务器连接尚未建立成功,会导致发包超时)
UNLOGIN
415 网络断开或者与网易云通信服务器建立连接失败 NET_BROKEN
416 请求过频错误,
为了防止开发者错误的调用导致服务器压力过大, SDK 做了频控限制,
并有一分钟的惩罚时间,过了惩罚时间后接口可以再次正常调用。
UNLOGIN
417 一般由一端登录导致自动登录失败导致。
这种情况发生于非强制登录模式下已有一端在线而当前设备进行自动登录(设置为只允许一端同时登录时),
出于安全方面的考虑,云信服务器判定当前端需要重新手动输入用户密码进行登录,故拒绝登录。
KICKOUT
1000 登录成功之前,调用本地数据库相关接口(手动登录的情况下数据库未打开) UNLOGIN

多端登录和互踢

多端登录

登录成功后,可以注册多端登录状态观察者。当有其他端登录或者注销时,会通过此接口通知到UI。登录成功后,如果有其他端登录着(在线),也会发出通知。返回的 OnlineClient 能够获取当前同时在线的客户端类型和操作系统。

/**
 * 注册/注销多端登录状态观察者。当有其他端登录或者注销时,会通过此接口通知到UI。
 * 登录成功后,如果有其他端登录着,也会发出通知。
 *
 * @param observer 观察者,参数为同时登录的其他端信息。
 *                 如果有其他端注销,参数为剩余的在线端。如果没有剩余在线端了,参数为null。
 * @param register true为注册,false为注销
 */
public void observeOtherClients(Observer<List<OnlineClient>> observer, boolean register);
参数 说明
observer 观察者,参数为同时登录的其他端信息。
如果有其他端注销,参数为剩余的在线端。
如果没有剩余在线端了,参数为 null。
register 是否注册观察者,注册为 true, 注销为 false

OnlineClient 接口说明:

返回值 方法 说明
String getOs() 客户端的操作系统信息
int getClientType() 客户端类型
long getLoginTime() 登录时间
String getClientIp() 客户端 IP
Observer<List<OnlineClient>> clientsObserver = new Observer<List<OnlineClient>>() {
        @Override
        public void onEvent(List<OnlineClient> onlineClients) {
            if (onlineClients == null || onlineClients.size() == 0) {
                return;
            }
            OnlineClient client = onlineClients.get(0);
            switch (client.getClientType()) {
                case ClientType.Windows:
                // PC端
                    breakcase ClientType.MAC:
                // MAC端
                    break;
                case ClientType.Web:
                // Web端
                    break;
                case ClientType.iOS:
                // IOS端
                    breakcase ClientType.Android:
                // Android端
                    break;
                default:
                    break;
            }
        }
    };

NIMClient.getService(AuthServiceObserver.class).observeOtherClients(clientsObserver, true);

互踢

网易云通信内置多端登录互踢策略为:移动端( Android 、 iOS )互踢,桌面端( PC 、 Mac 、 Web )互踢,移动端和桌面端共存( 可以采用 kickOtherClient 主动踢下共存的其他端)。

/**
 * 踢掉多端同时在线的其他端
 * @param client 被踢端信息
 * @return InvocationFuture 可设置回调函数,监听操作结果。
 */
public InvocationFuture<Void> kickOtherClient(OnlineClient client);
NIMClient.getService(AuthService.class).kickOtherClient(client).setCallback(new RequestCallback<Void>() {
    @Override
    public void onSuccess(Void param) {
        // 踢出其他端成功
    }

    @Override
    public void onFailed(int code) {
        // 踢出其他端失败,返回失败code
    }

    @Override
    public void onException(Throwable exception) {
        // 踢出其他端错误
    }
});

说明:当被其他端踢掉,可以通过在线状态观察者来接收监听消息,参考监听用户在线状态章节

如果当前的互踢策略无法满足业务需求的话,可以联系我们取消内置互踢,根据多端登录的回调和当前的设备列表,判断本设备是否需要被踢出。如果需要踢出,直接调用登出接口并在界面上给出相关提示即可。

登录状态

监听用户在线状态

登录成功后,SDK 会负责维护与服务器的长连接以及断线重连等工作。当用户在线状态发生改变时,会发出通知。此外,自动登录过程中也会有状态回调。

/**
 * 注册/注销在线状态变化观察者。
 * 注册后,Observer的onEvent方法会被立即调用一次,告知观察者当前状态。
 *
 * @param observer 观察者, 参数为当前状态
 * @param register true为注册,false为注销
 */
public void observeOnlineStatus(Observer<StatusCode> observer, boolean register);
StatusCode属性 说明
INVALID 未定义
UNLOGIN 未登录/登录失败
NET_BROKEN 网络连接已断开
CONNECTING 正在连接服务器
LOGINING 正在登录中
SYNCING 正在同步数据
LOGINED 已成功登录
KICKOUT 被其他端的登录踢掉
KICK_BY_OTHER_CLIENT 被同时在线的其他端主动踢掉
FORBIDDEN 被服务器禁止登录
VER_ERROR 客户端版本错误
PWD_ERROR 用户名或密码错误
NIMClient.getService(AuthServiceObserver.class).observeOnlineStatus(
    new Observer<StatusCode> () {
        public void onEvent(StatusCode status) {
            if (code.wontAutoLogin()) {
                // 被踢出、账号被禁用、密码错误等情况,自动登录失败,需要返回到登录界面进行重新登录操作
            }
        }
}, true);

被踢出的情况说明:

1. 当用户在线时被踢出,会立刻收到被踢出的状态变更通知;

2. 当用户离线后在其他设备成功登录,又在本设备重新自动登录时,也会收到被踢出的状态变更通知。

手动获取用户在线状态

开发者可以主动获取当前用户在线状态,建议使用监听的方式获取用户在线状态。

/**
 * 获取当前用户状态
 *
 * @return 当前状态
 */
public static StatusCode getStatus();
StatusCode status = NIMClient.getStatus();

监听数据同步状态

登录成功后,SDK 会立即同步数据(用户资料、用户关系、群资料、离线消息、漫游消息等),同步开始和同步完成都会发出通知。

/**
 * 注册/注销登录后同步数据过程通知
 *
 * @param observer 观察者,参数为同步数据的过程状态(开始/结束)
 * @param register true为注册,false为注销
 */
public void observeLoginSyncDataStatus(Observer<LoginSyncStatus> observer, boolean register);
LoginSyncStatus属性 说明
NO_BEGIN 未开始
BEGIN_SYNC 开始同步(正在同步)。
同步开始时,SDK 数据库中的数据可能还是旧数据。
(如果是首次登录,那么 SDK 数据库中还没有数据,
重新登录时 SDK 数据库中还是上一次退出时保存的数据)
在同步过程中,SDK 数据的更新会通过相应的监听接口发出数据变更通知。
SYNC_COMPLETED 同步完成 。SDK 数据库已完成更新
NIMClient.getService(AuthServiceObserver.class).observeLoginSyncDataStatus(new Observer<LoginSyncStatus>() {
    @Override
    public void onEvent(LoginSyncStatus status) {
        if (status == LoginSyncStatus.BEGIN_SYNC) {
            LogUtil.i(TAG, "login sync data begin");
        } else if (status == LoginSyncStatus.SYNC_COMPLETED) {
            LogUtil.i(TAG, "login sync data completed");
        }
    }
}, register);

一般来说, App 开发者在登录完成后可以开始构建数据缓存:

离线查看数据

对于一些弱 IM 场景,需要在登录成功前或者未登录状态下访问指定账号的数据(聊天记录、好友资料等)。 SDK 提供两种方案:

1. 使用自动登录。在登录成功前,可以访问 SDK 服务来读取本地数据(但不能发送数据)。

2. 使用 AuthService#openLocalCache 接口打开本地数据,这是个同步方法,打开后即可读取 SDK 数据库中的记录。可以通过注销来切换账号查看本地数据。

/**
 * 离线时打开本地数据
 * 适用场景:在手动登录没有成功前(可能由于网络问题,登录时间较长),可以访问SDK本地数据。
 * 此外,不调用本接口,采用自动登录也能达到同样的效果。
 *
 * @return 是否成功打开SDK本地数据
 */
public boolean openLocalCache(String account);
boolean res = NIMClient.getService(AuthService.class).openLocalCache(account);

断线重连机制

SDK 提供三种断线重连的策略(重新建立与网易云通信服务器的连接并重新登录):

1. 当网络由连通变为断开时,SDK 会启动立即上报网络断开的状态,并启动重连定时器,采用特定的策略并根据当前网络状态进行重连(如果 App 处于后台,重连时间间隔会较长)。

2. SDK会监听设备的网络连接状况,当监听到手机断网重连上网络的通知后,会立即进行重连并登录。

3. 应用长时间处于后台(后台进程可能活着但网络连接被系统切断)后切回到前台(恢复网络连通),SDK 监测到当前处于未登录状态,会在短时间内进行重连。