1、适用场景
联合登录模式是为游戏开发者提供的多账号体系登录方案,可最大化缩短用户登录链路,在保障账号安全性的同时,兼顾运营灵活性,适配多场景业务需求。
2、开发准备
- 阅读技术接入前准备v1.1,并于“小米开放平台-管理中心”获取核心参数并配置关键功能。
- 集成小米 Game Service SDK,具体参考:集成Game Service SDK
3、业务流程
3.1 交互示例

3.2 交互流程
3.2.1 小米系统账号登录场景

注意 !!!
1. 手机号不可作为账号唯一标识使用
- 存在部分场景下不会返回手机号。例如用户未授权、用户小米账号未绑定手机号、用户手机号变更,若游戏必须需要手机号进行注册,开发者需提供手机号收集页面,自行获取用户手机号。
- 小米账号对应手机号发生变更时,小米侧返回的uid不会发生变化
2. 小米实名服务不可作为唯一实名渠道 !!!
- 因存在用户未授权、授权失败等场景,开发者需在游戏内保留自主实名能力。
3. 游戏每次冷启动时,都需要调用unionLogin接口,unionLogin中包含合规流程,不可跳过。
3.2.2 游戏官方账号登录场景

4、接口说明
4.1 客户端接口
- 核心接口
| 接口名称 | 接口含义 | 接口参数 | 回调参数 | 调用时机 |
| com.xiaomi.ecosys.base.utils.RomUtils::isDeviceSupport 如果该接口返回true才需要调用下列所有接口,如果返回false,无需调用下列所有接口 | 设备判断接口:判断当前设备是否为国内小米设备 | 无 | boolean :true 国内小米设备 false 非国内小米设备 | 游戏启动后,调用其他接口之前 |
| com.xiaomi.ecosys.base.api.MiBasicApi::onUserAgreed 该接口全局仅需调用一次,建议在游戏存储隐私协议状态时调用 | 同意隐私协议接口:用户点击同意了游戏的隐私协议弹窗后调用 | Context:上下文 | 无 | 游戏启动后,调用isDeviceSupport判断当前设备为小米设备,用户首次同意隐私协议后调用一次 |
| com.xiaomi.ecosys.gameservice.MiGameService::init | 初始化接口:游戏CTA同意后调用,SDK完成初始化配置、游戏鉴权 | Activity:当前前台Activity InitCallback:初始化结果回调 | onInitSuccess:初始化成功 onError:初始化校验失败 errCode:错误码 errMsg:错误信息 | 用户同意隐私协议后,每次冷启动都需要调用一次 |
| com.xiaomi.ecosys.gameservice.MiGameService::unionLogin 注意区分调用接口,请勿使用@Deprecated接口 游戏每次冷启动时,都需要调用unionLogin接口 | 登录接口:支持用户通过系统小米账号快速完成账号授权,支持用户选择多种登录方式,并对用户进行合规校验 | Activity:当前前台Activity UnionLoginV2Param:登录参数 changeAccount:是否切换账号 UnionLoginV2Callback:登录回调 | onLoginSuccess:登录成功 UnionLoginV2Result code:登录结果码 0 成功 1 失败 2 取消 其他错误码参考开发者站文档 errMsg:登录异常描述 accountType:账号类型 official_account 官方账号登录 mi_account 小米账号登录 uid:用户id accountType为mi_account有效,用户唯一标识,游戏侧保证将uid与游戏内账号一一对应 serviceToken:登录token,用于后续的服务端请求参数 onLoginCancel:登录取消 onError 登录失败 errCode:错误码 errMsg:错误信息 | init初始化接口调用并回调初始化完成之后 |
| com.xiaomi.ecosys.gameservice.MiGameService::reportGameScene | 游戏关键场景上报:游戏侧上报指定场景到小米侧,小米侧将会基于游戏侧事件进行用户运营和系统侧优化 | sceneId:场景ID,具体定义如下 com.xiaomi.ecosys.gameservice.constants.GameScene *** 推荐上报场景 *** SCENE_LOGIN_FINISH = 1; //登录完成 *** 可选上报场景 *** SCENE_VIEW_PRODUCT_LIST = 2; //进入游戏商品页面 SCENE_RESOURCE_LOAD_BEGIN = 3; //资源加载开始 SCENE_RESOURCE_LOAD_END = 4; //资源加载完成 SCENE_HEAVY_LOAD = 5; //进入重负载场景 SCENE_MID_LOAD = 6; //进入中负载场景 SCENE_LITE_LOAD = 7; //进入轻负载场景 SCENE_EXIT_GAME = 8; //退出游戏 | 无 | 根据场景ID定义,在对应的场景下调用 |
注意
- 可选接口
| 接口名称 | 接口含义 | 接口参数 | 回调参数 | 调用时机 |
| com.xiaomi.ecosys.gameservice.MiGameService::authRealNameInfo 调用该接口前需确保游戏已在小米开放平台申请实名传输服务通过 | 实名信息授权:游戏侧在用户未实名的情况下,可以调用该接口完成用户实名信息授权,基于授权结果可以从小米服务端获取用户二要素信息 | Activity:当前前台Activity AuthRealNameParam:实名信息授权参数 gameId:游戏侧账号id,游戏侧用户唯一标识,必传字段、不可为空 AuthRealNameCallback:实名信息授权结果回调 | onAuthSuccess:授权成功 errCode:实名授权结果码 1 成功 2 取消 其他错误码参考开发者站文档 errMsg:实名授权描述 authCode:授权码,用于后续的服务器请求 expireTime:authCode过期时间,单位ms onAuthCancel:授权失败 onError:登录失败 errCode:错误码 errMsg:错误信息 | unionLogin接口调用完成后,游戏侧判断当前用户未实名时可选调用 |
| com.xiaomi.ecosys.gameservice.MiGameService::reportGameRole | 角色信息上报接口:游戏侧完成账号校验和登录流程后,客户端调用该接口上报游戏角色信息,核心作用为通知小米侧用户已进入游戏 | Activity:当前前台Activity MiRoleInfo:游戏角色信息 roleId:角色ID,必传字段、不可为空 roleName:角色名称,必传字段、不可为空 roleLevel:角色等级,非必传字段 realmId:区服ID,非必传字段 realmName:区服名称,非必传字段 cpOpenId:CP账号ID: 非必传字段 上述所有字段长度不可超过100个字符 cpChannel:游戏侧渠道号 ReportGameRoleCallback:角色上报结果回调 | onReportSuccess:角色上报完成 onError:角色上报失败(不阻塞主流程) errCode:错误码 errMsg:错误信息 | 游戏侧完成用户登录并获取到用户的游戏角色信息后,代表正式进入游戏 |
| com.xiaomi.ecosys.gameservice.MiGameService::registerAntiListener | 监听未成年人防沉迷状态:注册监听器获取实时未成年人剩余可玩时间 游戏侧可基于未成年人剩余可玩时间调整游戏内功能,例如限制开启新的对局 | GameAntiStatusCallback:防沉迷状态回调 | isNeedLimit:用户实名信息 0-未知 1-成年人 2-未成年人 dayLeftMills 用户剩余可玩时间 | init初始化接口调用并回调初始化完成之后 |
4.2 服务端接口
5、开发流程
5.1 接口调用流程图

5.2 开发步骤
5.2.1 设备判断
调用SDK的所有接口之前应先调用com.xiaomi.ecosys.base.utils.RomUtils.isDeviceSupport(),判断当前设备是否为国内版小米设备,如果是国内版小米设备,则继续调用后续初始化、登录接口,否则非小米设备则走游戏自身的登录流程。
/**
* true:国内版小米设备
*/
RomUtils.isDeviceSupport();5.2.2 同意隐私协议
根据个保法要求,在用户同意隐私协议前不允许获取用户信息及上报任何手机设备上的信息,请在用户同意隐私后调用以下接口,否则无法正常调用其他接口,调用时会抛出异常。该接口会保存用户同意隐私协议的状态,无需多次调用。
/**
* 同意隐私协议后调用该接口
*/
MiBasicApi.getInstance().onUserAgreed(Context context);注意!!!
- SDK本身不会弹出任何隐私协议,隐私协议由游戏开发者自行设计实现,但需要在游戏隐私协议中附加Game Service SDK的隐私协议:https://privacy.mi.com/ecosys-sdk/zh_CN/
- 请在提审前,保证游戏在用户同意隐私协议前不会获取任何手机信息和发送网络请求,否则在审核时会被驳回。建议在 小米隐私合规检测平台 测试通过后再提交审核。
5.2.3 初始化
调用init接口初始化Game Service SDK
MiGameService.getInstance().init(this, new InitCallback() {
@Override
public void onInitSuccess() {
Toast.makeText(MainActivity.this, "初始化成功", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(int errCode, String errMsg) {
Toast.makeText(MainActivity.this, "初始化失败 errCode:"+errCode+"| errMsg:"+errMsg, Toast.LENGTH_SHORT).show();
}
});5.2.4 发起登录
调用unionLogin接口发起联合登录,接口包含了实名和防沉迷的校验流程。
UnionLoginV2Param loginParam = UnionLoginV2Param.builder()
.changeAccount(false) //是否切换账号 默认为false
.build();
MiGameService.getInstance().unionLogin(MainActivity.this, loginParam, new UnionLoginV2Callback() {
@Override
public void onLoginSuccess(UnionLoginV2Result result) {
if (result != null) {
//登录方式
String accountType = loginResult.getAccountType();
//用户ID
String uid = loginResult.getUid();
//登录token
String serviceToken = loginResult.getServiceToken();
//登录完成后由服务端完成账号信息校验和用户信息获取
}
}
@Override
public void onLoginCancel() {
Toast.makeText(MainActivity.this, "登录取消", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(int errCode, String errMsg) {
Toast.makeText(MainActivity.this, "登录失败 errCode:"+errCode+"| errMsg:"+errMsg, Toast.LENGTH_SHORT).show();
}
});5.2.5 游戏场景上报
游戏客户端上报指定场景到小米侧,小米侧将会基于游戏侧事件进行用户运营和系统侧优化
其中登录完成场景(SCENE_LOGIN_FINISH)推荐上报
//上报游戏侧完成登录
MiGameService.getInstance().reportGameScene(GameScene.SCENE_LOGIN_FINISH);
// 推荐上报场景
//场景ID 登录完成
public static final int SCENE_LOGIN_FINISH = 1;
// 可选上报场景
//场景ID 进入游戏商品页面
public static final int SCENE_VIEW_PRODUCT_LIST = 2;
//场景ID 资源加载开始
public static final int SCENE_RESOURCE_LOAD_BEGIN = 3;
//场景ID 资源加载完成
public static final int SCENE_RESOURCE_LOAD_END = 4;
//场景ID 进入重负载场景
public static final int SCENE_HEAVY_LOAD = 5;
//场景ID 进入中负载场景
public static final int SCENE_MID_LOAD = 6;
//场景ID 进入轻负载场景
public static final int SCENE_LITE_LOAD = 7;
//场景ID 退出游戏
public static final int SCENE_EXIT_GAME = 8;5.2.6 切换账号(可选)
游戏内可为已登录的用户提供切换账号的入口,用户点击后,调用unionLogin接口并将参数changeAccount指定为true
UnionLoginV2Param loginParam = UnionLoginV2Param.builder()
.changeAccount(true) //是否切换账号 默认为false
.build();
MiGameService.getInstance().unionLogin(MainActivity.this, loginParam, new UnionLoginV2Callback() {
@Override
public void onLoginSuccess(UnionLoginV2Result result) {
if (result != null) {
//登录方式
String accountType = loginResult.getAccountType();
//用户ID
String uid = loginResult.getUid();
//登录token
String serviceToken = loginResult.getServiceToken();
//登录完成后由服务端完成账号信息校验和用户信息获取
}
}
@Override
public void onLoginCancel() {
Toast.makeText(MainActivity.this, "登录取消", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(int errCode, String errMsg) {
Toast.makeText(MainActivity.this, "登录失败 errCode:"+errCode+"| errMsg:"+errMsg, Toast.LENGTH_SHORT).show();
}
});5.2.7 实名信息授权(可选)
游戏侧完成账号查询/注册后,若判断该用户未实名,可调用authRealNameInfo接口发起实名授权,授权通过后可通过服务端接口获取到用户的实名信息。如果实名授权失败,游戏客户端需自行定义兜底处理逻辑。
注:调用该接口前需确保游戏已在小米开放平台完成实名数据传输服务鉴权申请
注:当用户取消授权或授权失败时,游戏需自行引导用户输入实名二要素信息
AuthRealNameParam authRealNameParam = AuthRealNameParam.builder()
.gameId(gameAccountId) //游戏侧唯一标识
.build();
MiGameService.getInstance().authRealNameInfo(this, authRealNameParam, new AuthRealNameCallback() {
@Override
public void onAuthSuccess(AuthRealNameResult result) {
if (result != null && result.isValid()){
//结果码 1 成功 2 取消
int errCode = result.getErrCode();
//错误信息
String errMsg = result.getErrMsg();
//授权码
String authCode = result.getAuthCode();
//授权码过期时间
String expireTime = result.getExpireTime();
if (errCode == 1) {
// 请求服务端获取实名二要素信息
} else {
// 展示游戏内收集用户实名信息页面
}
} else {
// 展示游戏内收集用户实名信息页面
}
}
@Override
public void onAuthCancel() {
Toast.makeText(UnionLoginV2Activity.this, "实名授权取消", Toast.LENGTH_SHORT).show();
}
@Override
public void onError(int errCode, String errMsg) {
Toast.makeText(UnionLoginV2Activity.this, "实名授权失败 errCode:"+errCode+"| errMsg:"+errMsg, Toast.LENGTH_SHORT).show();
}
});5.2.8 角色信息上报(可选)
游戏侧完成角色创建后上报角色信息至小米侧
//角色信息
MiRoleInfo miRoleInfo = new MiRoleInfo();
miRoleInfo.setRoleId("角色id"); //必传字段
miRoleInfo.setRoleName("角色名称"); //必传字段
miRoleInfo.setRoleLevel("角色等级"); //非必传字段
miRoleInfo.setRealmId("区服ID"); //非必传字段
miRoleInfo.setRealmName("区服名称"); //非必传字段
miRoleInfo.setCpOpenId("CP账号ID"); //非必传字段
//游戏侧渠道ID
String cpChannel = "channel_xxx";
MiGameService.getInstance().reportGameRole(this, miRoleInfo, cpChannel, new ReportGameRoleCallback() {
@Override
public void onError(int errCode, String errMsg) {
Toast.makeText(AfterLoginActivity.this, "上报失败 errCode:"+errCode+"| errMsg:"+errMsg, Toast.LENGTH_SHORT).show();
}
@Override
public void onReportSuccess() {
Toast.makeText(AfterLoginActivity.this, "上报成功", Toast.LENGTH_SHORT).show();
}
});5.2.9 监听未成年防沉迷状态(可选)
Game Service SDK会对未成年用户的游戏时间进行监控,如游戏侧需要监听用户剩余可玩时长,可通过调用registerAntiListener接口获取用户剩余可玩时长,游戏侧可根据剩余可玩时长给予用户提示或限制部分用户行为
MiGameService.getInstance().registerAntiListener(new GameAntiStatusCallback() {
@Override
public void onUserAntiChange(int isNeedLimit, long dayLeftMills) {
Toast.makeText(LoginActivity.this, "防沉迷状态更新 用户实名状态:"+isNeedLimit+"| 剩余可玩时间:"+dayLeftMills, Toast.LENGTH_SHORT).show();
}
@Override
public void onError(int errCode, String errMsg) {
Toast.makeText(LoginActivity.this, "注册监听失败 errCode:"+errCode+"| errMsg:"+errMsg, Toast.LENGTH_SHORT).show();
}
});5.3 SDK错误码
| 错误码 | 相关接口 | 错误码含义 |
| 10001 | ALL | 当前未完成初始化 |
| 10003 | ALL | 网络状态异常 |
| 10007 | ALL | 参数错误 |
| 10008 | ALL | 内部异常 |
| 10009 | unionLogin | 登录频率限制 |
| 10010 | unionLogin | 登录失败 |
| 10012 | init | AppId参数不合法,登录参数未正常配置,请参考技术接入前准备.v1.1完成配置 |
| 10013 | ALL | 当前软件版本不支持 |
| 10022 | ALL | 当前账号为空 |
| 10023 | unionLogin | 当前账号不支持关联 |
| 10024 | unionLogin | 用户未登录 |
| -10003 | ALL | 系统内部错误 |
| 10030 | reportGameRole | 角色上报失败 |