多端登录管理

更新时间:2025-11-20 10:07:45下载pdf

概述

多端登录功能允许用户查看当前账号已在哪些设备上登录,并支持远程登出指定设备。本文档介绍如何使用 SDK 提供的多端登录相关接口。

功能说明

多端登录功能包含以下核心接口:

  • getUserLoginMultiTerminal:获取当前账号在哪些设备上登录。
  • getMultiTerminalLogoutCode:获取设备登出校验码 loginOutCode
  • logoutMultiTerminal:指定登出某台设备。
  • registerTerminalLogoutListener:注册账号被登出监听(当账号在其他设备上被登出时接收通知)。

接口说明

获取登录设备列表

方法签名

void getUserLoginMultiTerminal(IUserBusinessCallback<UserLoginMultiterminalVO> callback)

功能描述

获取当前账号在所有设备上的登录信息,包括设备 ID、操作系统、平台、登录状态和登录时间等。

参数说明

参数 类型 说明
callback IUserBusinessCallback 回调接口,返回登录设备列表

回调接口

IUserBusinessCallback<UserLoginMultiterminalVO>

public interface IUserBusinessCallback<ResponseValue> {
    /**
     * 成功回调
     * @param success 返回 UserLoginMultiterminalVO 对象
     */
    void onSuccess(ResponseValue success);

    /**
     * 失败回调
     * @param code 错误码
     * @param error 错误信息
     */
    void onError(String code, String error);
}

返回数据模型

UserLoginMultiterminalVO

public class UserLoginMultiterminalVO {
    private List<UserLoginMultiterminalBean> terminalLoginList;

    public List<UserLoginMultiterminalBean> getTerminalLoginList() {
        return terminalLoginList;
    }
}

UserLoginMultiterminalBean

public class UserLoginMultiterminalBean {
    private String terminalId;      // 设备唯一标识
    private String os;              // 操作系统
    private String osSystem;        // 操作系统版本
    private String platform;        // 平台信息
    private Integer status;         // 会话状态:0 - 无效,1 - 有效
    private Long loginTime;        // 登录时间(时间戳)

    // Getter 和 Setter 方法
    public String getTerminalId() { return terminalId; }
    public String getOs() { return os; }
    public String getOsSystem() { return osSystem; }
    public String getPlatform() { return platform; }
    public Integer getStatus() { return status; }
    public Long getLoginTime() { return loginTime; }
}

使用示例

ThingHomeSdk.getUserInstance().getUserLoginMultiTerminal(object : IUserBusinessCallback<UserLoginMultiterminalVO> {
    override fun onSuccess(result: UserLoginMultiterminalVO) {
        val deviceList = result.terminalLoginList
        if (!deviceList.isNullOrEmpty()) {
            for (device in deviceList) {
                Log.d("MultiTerminal", "设备ID: ${device.terminalId}")
                Log.d("MultiTerminal", "操作系统: ${device.os}")
                Log.d("MultiTerminal", "平台: ${device.platform}")
                Log.d("MultiTerminal", "状态: ${if (device.status == 1) "有效" else "无效"}")
                Log.d("MultiTerminal", "登录时间: ${Date(device.loginTime)}")
            }
        }
    }

    override fun onError(code: String, error: String) {
        Log.e("MultiTerminal", "获取设备列表失败: $code - $error")
    }
})

获取设备登出校验码

方法签名

void getMultiTerminalLogoutCode(String countryCode, String username, String password, 
                                String code, Boolean ifencrypt, 
                                Integer type, Integer verifyType, 
                                IUserBusinessCallback<MultiTerminalBean> callback)

功能描述

在进行设备登出操作前,需要先进行账号验证,验证通过后获取登出校验码 loginOutCode。该校验码用于后续的登出操作。

密码校验和验证码校验,都只需直接调用该方法,无需预先调用其他接口。

参数说明

参数 类型 必填 说明
countryCode String 国家区号,例如:“86”
username String 用户名(手机号或邮箱)
password String 条件必填 密码(当 verifyType = 1 时必填,当 verifyType = 2 时可为空)
code String 条件必填 验证码(当 verifyType = 2 时必填,当 verifyType = 1 时为空)
ifencrypt Boolean 密码是否加密(当 verifyType = 1 时必须为 true,当 verifyType = 2 时可传 false
type Integer 账号类型:1 - 手机号,2 - 邮箱
verifyType Integer 验证方式:1 - 密码验证,2 - 验证码验证
callback IUserBusinessCallback 回调接口

验证方式说明

  • 方式一:密码验证(verifyType = 1)

    • usernamepassword:必填
    • ifencrypt:必须为 true
    • code:传 null

    直接调用该方法即可完成验证并获取 loginOutCode

  • 方式二:验证码验证(verifyType = 2)

    • usernamecode:必填
    • password:可不填写(传 null
    • ifencrypt:传 false

    使用验证码验证时,需要先调用 sendVerifyCodeWithUserName 发送验证码(type = 8),获取验证码后再调用本方法。

返回数据模型

MultiTerminalBean

public class MultiTerminalBean {
    private String loginOutCode;    // 登出校验码
    private long expireTime;        // 校验码过期时间(秒级时间戳)

    public String getLoginOutCode() {
        return loginOutCode;
    }

    public long getExpireTime() {
        return expireTime;
    }
}

使用示例

示例 1:使用密码验证

val countryCode = "86"
val username = "13800138000"  // 手机号或邮箱
val password = "your_password"
val type = 1  // 1 - 手机号,2 - 邮箱
val verifyType = 1  // 1 - 密码验证

ThingHomeSdk.getUserInstance().getMultiTerminalLogoutCode(
    countryCode,
    username,
    password,
    null,  // code
    true,   // ifencrypt 必须为 true
    type,
    verifyType,
    object : IUserBusinessCallback<MultiTerminalBean> {
        override fun onSuccess(result: MultiTerminalBean) {
            val loginOutCode = result.loginOutCode
            val expireTime = result.expireTime
            Log.d("MultiTerminal", "登出校验码: $loginOutCode")
            Log.d("MultiTerminal", "过期时间: ${Date(expireTime * 1000)}")
            // 保存 loginOutCode,用于后续登出操作
        }

        override fun onError(code: String, error: String) {
            Log.e("MultiTerminal", "获取登出校验码失败: $code - $error")
        }
    }
)

示例 2:使用验证码验证

// 步骤 1:发送验证码
val countryCode = "86"
val username = "13800138000"
val type = 1  // 1 - 手机号,2 - 邮箱

ThingHomeSdk.getUserInstance().sendVerifyCodeWithUserName(
    username,
    null,  // region
    countryCode,
    8,     // type = 8,表示登出账号验证验证码
    object : IResultCallback {
        override fun onSuccess() {
            // 验证码发送成功,提示用户输入验证码
        }

        override fun onError(code: String, error: String) {
            Log.e("MultiTerminal", "发送验证码失败: $code - $error")
        }
    }
)

// 步骤 2:用户输入验证码后,获取登出校验码
val verifyCode = "123456"  // 用户输入的验证码
val verifyType = 2  // 2 - 验证码验证

ThingHomeSdk.getUserInstance().getMultiTerminalLogoutCode(
    countryCode,
    username,
    null,  // password 验证码验证时可不填写
    verifyCode,  // code 验证码
    false, // ifencrypt
    type,
    verifyType,
    object : IUserBusinessCallback<MultiTerminalBean> {
        override fun onSuccess(result: MultiTerminalBean) {
            val loginOutCode = result.loginOutCode
            // 保存 loginOutCode,用于后续登出操作
        }

        override fun onError(code: String, error: String) {
            Log.e("MultiTerminal", "获取登出校验码失败: $code - $error")
        }
    }
)

登出指定设备

方法签名

void logoutMultiTerminal(String terminalId, String loginOutCode, IBooleanCallback callback)

功能描述

使用设备 ID 和登出校验码,远程登出指定的设备。登出成功后,该设备将无法继续使用当前账号。

参数说明

参数 类型 必填 说明
terminalId String 设备唯一标识,从 getUserLoginMultiTerminal 获取
loginOutCode String 登出校验码,从 getMultiTerminalLogoutCode 获取
callback IBooleanCallback 回调接口

回调接口

IBooleanCallback

public interface IBooleanCallback {
    /**
     * 成功回调
     */
    void onSuccess();

    /**
     * 失败回调
     * @param code 错误码
     * @param error 错误信息
     */
    void onError(String code, String error);
}

使用示例

// terminalId 从 getUserLoginMultiTerminal 获取
val terminalId = "device_terminal_id_12345"
// loginOutCode 从 getMultiTerminalLogoutCode 获取
val loginOutCode = "logout_code_abc123"

ThingHomeSdk.getUserInstance().logoutMultiTerminal(terminalId, loginOutCode, object : IBooleanCallback {
    override fun onSuccess() {
        Log.d("MultiTerminal", "设备登出成功")
        // 可以刷新设备列表,确认设备已登出
    }

    override fun onError(code: String, error: String) {
        Log.e("MultiTerminal", "设备登出失败: $code - $error")
    }
})

账号被登出监听

功能描述

当账号在其他设备上被登出时,当前设备可以通过监听器接收到通知。这能有效满足及时响应用户在其他设备上的登出操作的需求,例如:清理本地缓存、跳转到登录页面等。

使用方式

请注意,该功能需要依赖 thingsmart-businessapiextensionkit 模块。

方法签名

fun registerTerminalLogoutListener(callback: INotificationCallback)
fun unregisterTerminalLogoutListener()

回调接口

INotificationCallback

interface INotificationCallback {
    fun onSuccess()
}

使用示例

import com.thingclips.businessapiextensionkit.ThingSmartExtNotificationFunc
import com.thingclips.businessapiextensionkit.callback.INotificationCallback

// 注册监听
ThingSmartExtNotificationFunc.registerTerminalLogoutListener(object : INotificationCallback {
    override fun onSuccess() {
        // 账号在其他设备上被登出
        Log.d("MultiTerminal", "账号被其他设备登出")
        // 执行清理操作,例如:
        // 1. 清理本地用户数据
        // 2. 跳转到登录页面
        // 3. 清理缓存等
        handleTerminalLogout()
    }
})

// 注销监听(在不需要时调用,例如 Activity 销毁时)
ThingSmartExtNotificationFunc.unregisterTerminalLogoutListener()

完整示例:在 Activity 中使用

class MainActivity : AppCompatActivity() {
    private var terminalLogoutCallback: INotificationCallback? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 确保已初始化(如果用户刚登录,需要先初始化)
        if (!ThingSmartExtNotificationFunc.isInitialized()) {
            ThingSmartExtNotificationFunc.initialize()
        }

        // 注册多终端登出监听
        terminalLogoutCallback = object : INotificationCallback {
            override fun onSuccess() {
                runOnUiThread {
                    // 账号被其他设备登出,执行清理操作
                    Log.d("MultiTerminal", "账号被其他设备登出")

                    // 清理用户数据
                    ThingHomeSdk.getUserInstance().removeUser()

                    // 跳转到登录页面
                    val intent = Intent(this@MainActivity, LoginActivity::class.java)
                    intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
                    startActivity(intent)
                    finish()
                }
            }
        }

        ThingSmartExtNotificationFunc.registerTerminalLogoutListener(terminalLogoutCallback!!)
    }

    override fun onDestroy() {
        super.onDestroy()
        // 注销监听,避免内存泄漏
        terminalLogoutCallback?.let {
            ThingSmartExtNotificationFunc.unregisterTerminalLogoutListener()
        }
    }
}

初始化说明

当用户退出登录时,ThingSmartExtNotificationFunc 的资源会被自动销毁。因此,在用户登录成功后,需要重新调用 initialize() 方法进行初始化,然后才能注册监听器。

初始化示例

// 在用户登录成功后调用
ThingSmartExtNotificationFunc.initialize()

// 初始化完成后,注册监听器
ThingSmartExtNotificationFunc.registerTerminalLogoutListener(object : INotificationCallback {
    override fun onSuccess() {
        // 处理账号被登出的逻辑
    }
})

完整示例:在登录成功后初始化

// 登录成功回调中
fun onLoginSuccess() {
    // 1. 初始化多终端登出监听功能
    ThingSmartExtNotificationFunc.initialize()

    // 2. 注册监听器
    ThingSmartExtNotificationFunc.registerTerminalLogoutListener(object : INotificationCallback {
        override fun onSuccess() {
            // 账号在其他设备上被登出
            handleTerminalLogout()
        }
    })
}

注意事项

  • 资源销毁机制:当前账号退出登录时,ThingSmartExtNotificationFunc 的资源会被自动销毁(调用 destroy() 方法),此时 isInitialized 会被设置为 false

  • 登录后重新初始化:由于退出登录时资源会被销毁,因此在用户重新登录成功后,必须重新调用 initialize() 方法进行初始化,然后才能注册监听器。否则监听器无法正常工作。

  • 监听器注册时机:建议在用户登录成功后立即调用 initialize() 初始化,然后注册监听器,确保能够及时接收到登出通知。

  • 监听器注销:在不需要监听时(例如 Activity 销毁、用户退出登录等),务必调用 unregisterTerminalLogoutListener() 注销监听,避免内存泄漏。

  • 线程安全onSuccess() 回调可能不在主线程执行,如果需要在回调中更新 UI,请使用 runOnUiThread()Handler 切换到主线程。


完整使用流程

场景:用户查看登录设备并登出其他设备

class MultiTerminalManager {
    private val userManager: IThingUser = ThingHomeSdk.getUserInstance()

    /**
     * 步骤 1:获取登录设备列表
     */
    fun getLoginDevices() {
        userManager.getUserLoginMultiTerminal(object : IUserBusinessCallback<UserLoginMultiterminalVO> {
            override fun onSuccess(result: UserLoginMultiterminalVO) {
                val deviceList = result.terminalLoginList
                // 显示设备列表给用户
                displayDeviceList(deviceList)
            }

            override fun onError(code: String, error: String) {
                Log.e("MultiTerminal", "获取设备列表失败: $code - $error")
            }
        })
    }

    /**
     * 步骤 2:用户选择要登出的设备,进行账号验证并获取登出校验码
     * 使用密码验证方式
     */
    fun prepareLogoutDeviceWithPassword(
        terminalId: String,
        countryCode: String,
        username: String,
        password: String,
        type: Int
    ) {
        userManager.getMultiTerminalLogoutCode(
            countryCode,
            username,
            password,
            null,  // code
            true,   // ifencrypt 密码验证时必须为 true
            type,
            1,      // 使用密码验证
            object : IUserBusinessCallback<MultiTerminalBean> {
                override fun onSuccess(result: MultiTerminalBean) {
                    val loginOutCode = result.loginOutCode
                    // 步骤 3:执行登出操作
                    logoutDevice(terminalId, loginOutCode)
                }

                override fun onError(code: String, error: String) {
                    Log.e("MultiTerminal", "获取登出校验码失败: $code - $error")
                }
            }
        )
    }

    /**
     * 步骤 2:用户选择要登出的设备,进行账号验证并获取登出校验码
     * 使用验证码验证方式
     */
    fun prepareLogoutDeviceWithCode(
        terminalId: String,
        countryCode: String,
        username: String,
        verifyCode: String,
        type: Int
    ) {
        userManager.getMultiTerminalLogoutCode(
            countryCode,
            username,
            null,  // password 验证码验证时可不填写
            verifyCode,  // code
            false, // ifencrypt
            type,
            2,     // 使用验证码验证
            object : IUserBusinessCallback<MultiTerminalBean> {
                override fun onSuccess(result: MultiTerminalBean) {
                    val loginOutCode = result.loginOutCode
                    // 步骤 3:执行登出操作
                    logoutDevice(terminalId, loginOutCode)
                }

                override fun onError(code: String, error: String) {
                    Log.e("MultiTerminal", "获取登出校验码失败: $code - $error")
                }
            }
        )
    }

    /**
     * 步骤 3:登出指定设备
     */
    private fun logoutDevice(terminalId: String, loginOutCode: String) {
        userManager.logoutMultiTerminal(terminalId, loginOutCode, object : IBooleanCallback {
            override fun onSuccess() {
                Log.d("MultiTerminal", "设备登出成功")
                // 刷新设备列表
                getLoginDevices()
            }

            override fun onError(code: String, error: String) {
                Log.e("MultiTerminal", "设备登出失败: $code - $error")
            }
        })
    }

    private fun displayDeviceList(deviceList: List<UserLoginMultiterminalBean>?) {
        // 在 UI 上显示设备列表
    }
}

注意事项

登录状态要求

调用这些接口前,需要确保用户已登录(ThingHomeSdk.getUserInstance().isLogin() 返回 true)。

校验码有效期

loginOutCode 有时效性,从 getMultiTerminalLogoutCode 返回的 expireTime 可以获取过期时间。建议在获取后尽快使用。

设备标识

terminalId 是设备的唯一标识,用于区分不同的登录设备。同一个账号在不同设备上登录会有不同的 terminalId

验证方式选择:

  • 密码验证(verifyType = 1):
    • usernamepassword:必填
    • ifencrypt:必须为 true
    • code:传 null
    • 直接调用 getMultiTerminalLogoutCode 即可,无需预先调用其他接口
  • 验证码验证(verifyType = 2):
    • usernamecode:必填
    • password:可不填写(传 null
    • ifencrypt:传 false
    • 需要先调用 sendVerifyCodeWithUserName 发送验证码(type=8),获取验证码后再调用本方法

错误处理

所有接口都提供了错误回调,建议在 UI 上给用户友好的错误提示。

当前设备识别

可以通过对比 terminalId 与当前设备的标识来判断是否为当前设备,避免误登出当前正在使用的设备。

账号被登出监听

建议在应用启动时注册 registerTerminalLogoutListener 监听器,以便及时响应账号在其他设备上被登出的情况,及时清理本地数据并引导用户重新登录。