# 客户端接入指南 --- 本文档面向 Android 开发者,描述的接入方式适合运营在 4399 平台的联机游戏。 | 版本列表 | 变更时间 | 变更内容 | |-----------|----------------|------------------------------------------------------------------------------| | `v3.7.1` | **2023.01.09** | - | | `v3.10.0` | **2023.02.16** | 接口不变,若打包出现资源错误,**`compileSdkVersion`需要提升到29或更高** | | | `v3.12.1` | **2023.05.15** | 接口不变,但游戏 Activity 的 `android:configChanges` 要增加配置以支持分屏,具体内容见初始化部分 | | `v3.12.2` | **2023.06.14** | 接口不变,优化充值页面折叠屏兼容性 | | `v3.13.0` | **2023.07.10** | 新增好友、排行、成就接口 | | `v3.14.0` | **2023.09.05** | - | | `v3.14.2` | **2023.11.01** | 接口不变,更新了一键登录SDK,`AndroidManifest.xml, assets资源有更新`,使用jar+res方式接入须注意 | | `v3.14.4` | **2024.01.12** | 接口不变,升级一键登录SDK依赖,`AndroidManifest.xml, assets/res资源, so等有更新`,使用jar+res方式接入须注意 | | `v3.14.5` | **2024.09.20** | - | | `v3.15.0` | **2024.10.11** | - | | `v3.15.1` | **2024.10.18** | - | | `v3.16.0` | **2025.03.17** | 移除 `PopLogoStyle.POPLOGOSTYLE_FOUR` 选项,更新悬浮窗图标主题
增加跳转游戏盒更新接口,用法见游戏更新 | ## SDK 下载 SDK和相关示例的下载地址可在游戏创建后,进入 **「游戏管理-SDK服务」** 页面获取。 image ## SDK 说明 ### 用户信息与隐私策略

注意

如果您是开发者,在为用户提供服务前请阅读 《4399通行证用户服务协议》《隐私政策》,了解SDK对个人信息收集范围、处理目的以及权限使用情况。请您向用户提供服务时,告知相关信息并取得用户同意。

如果您是用户,请在使用我们的服务前阅读 《4399通行证用户服务协议》《隐私政策》,了解SDK对个人信息收集范围、处理目的以及权限使用情况。充分理解后再开始使用我们的服务。

### 功能描述 4399 运营 SDK(以下简称 SDK),为接入的游戏提供4399账户登录、充值、更新、游戏内容展示等功能。 ### SDK 组成 #### 客户端 SDK SDK 优先提供在线`aar`依赖方式,其 Demo 结构遵循 Android Studio(as) 规范,但仍然保留了 `jar+res` 的依赖方式。 SDK 支持的编译配置 **android:minSdkVersion >= 16**。 #### 服务端 API 需要充值功能、登录服务端校验,参考 [SDK 服务端接入文档](https://sdkftp.4399doc.com/external/operate/3.16.0/server_api_1.html) ## 集成流程 ### 准备 首次接入 SDK,要在 [4399 开放平台](https://open.4399.cn/) 注册应用,主要是提交APK、素材,配置游戏币、充值回调地址等信息。 完成后,开发者将得到 SDK 的基础参数:`game key`或`GameKey`,游戏在 4399 平台的运营标识(开发者平台的游戏信息页面显示为“游戏ID”) ### 引入依赖 根据游戏需要,以下三种方式可选其一 - *在线aar* 若使用*在线aar*,开发者只要在使用 SDK 的 module 的 `build.gradle`中引入以下内容即可 ```groovy repositories { maven { // 4399 SDK 开放仓库:正式 url 'https://mvn.4399doc.com/repository/maven-releases' } maven { // 4399 SDK 开放仓库:快照 url 'https://mvn.4399doc.com/repository/maven-snapshots' } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) // 运营 SDK:建议使用最新正式版本(release仓库中的版本),可通过浏览仓库地址或向运营咨询版本 implementation "cn.m4399.sdk:operate:3.16.0" // volley 和 support 是 SDK 使用的外部依赖,若接入方已有,可忽略 implementation 'com.android.volley:volley:1.2.1' //noinspection GradleCompatible implementation "com.android.support:support-v4:28.0.0" } ``` *注意:若使用`7.0+`版本的 gradle 及 android build 插件,仓库地址应配置在`settings.gradle`中* - *本地aar* 若使用*本地aar*,则要下载 [在线aar](https://mvn.4399doc.com/repository/maven-releases/cn/m4399/sdk/operate/3.16.0/operate-3.16.0.aar) 和 [jar+res](https://sdkftp.4399doc.com/external/operate/3.16.0/operate-3.16.0+653.zip) 到本地,并将`aar`和`jar`包中的以下文件 拷贝到游戏模块的`libs`目录下 ``` operate/libs/volley-v1.2.1.jar operate/libs/support-v13-23.2.1.jar ``` Gradle 文件中注意添加本地依赖 ```groovy dependencies { implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) } ``` - *jar+res* [jar+res 依赖引入](https://sdkftp.4399doc.com/external/operate/3.16.0/jar_res_import.html) 比较繁琐,但是完成后,后续接口调用仍然是一样的。 #### abi 适配与 so 库 若游戏不需要支持所有 [abi](https://developer.android.google.cn/ndk/guides/abis?hl=zh-cn) ,可以按需选用。 `aar`依赖方式,按以下方式配置需要的`abi`;`jar+res`依赖方式需要手动删除**不需要**的`abi`目录 ``` android { defaultConfig { ndk { // 根据游戏需要选择 abiFilters "armeabi", "armeabi-v7a", "x86", "arm64-v8a" } } } ``` ### 接口调用:初始化(必须) **功能简介:** 此接口初始化 SDK 内部状态,并完成“更新、激活、防沉迷”等流程。 **SDK 初始化后才能正确调用其他接口** **调用时机:** 建议在游戏 `Activity` 的开始处如 `onCreate` 方法中进行 ```java private void initSDK() { mOpeCenter = OperateCenter.getInstance(); OperateConfig config = new OperateConfig.Builder(MainActivity.this) // 设置调试模式,可选,默认false, 发布前应设置为false或删除调用 .setDebugEnabled(false) // 设置游戏运营 key,必须 .setGameKey(GAME_KEY) // 设置 SDK 页面方向,必须,应与游戏方向一致,可取值见下文说明 .setOrientation(GAME_ORIENTATION) // 设置悬浮窗风格,可选,悬浮窗更多信息参考下文 .setPopLogoStyle(PopLogoStyle.POPLOGOSTYLE_ONE) // 设置悬浮窗初始位置,可选,有四种,分别在屏幕上下左右 .setPopWinPosition(PopWinPosition.POS_LEFT) // 设置游戏充值是否支持超出金额,可选,true支持,默认false .setSupportExcess(GAME_SUPPORT_EXCESS) // 设置是否在 Android 9.+系统上兼容全面屏,true兼容,默认false .compatNotch(GAME_COMPACT_NOTCH) .build(); mOpeCenter.setConfig(config); // 注意:初始化完成后,其他接口才可用 mOpeCenter.init(MainActivity.this, new OperateCenter.OnInitGlobalListener() { /* * 初始化回调 * 只有在init之后, isLogin()返回的状态才可靠 */ @Override public void onInitFinished(boolean isLogin, User userInfo) { // 初始化后处理 } /* * 登出账号的回调 * @param fromUserCenter 标识区分是否从个人中心中登出的,若是则为true,不是为false */ @Override public void onUserAccountLogout(boolean fromUserCenter) { // 用户登出,游戏应回到自身登录页面 } /* * 切换账号回调 */ @Override public void onSwitchUserAccountFinished(boolean fromUserCenter, User userInfo) { // 用户切换,游戏应回到选服页面 } }); } ``` 初始化的几点说明 #### 页面方向 - *游戏页面* 应如下配置`android:configChanges`等,否则系统强制方向变化(一般发生是打开外部应用,如支付宝、微信)或系统设置变化时,可能会发生异常 ```xml ``` 其中`android:configChanges` 新增的配置项,是为适配 Android 分屏模式而添加的,更多内容参考[多窗口支持](https://developer.android.google.cn/guide/topics/ui/multi-window?hl=zh-cn#config) **分屏(画中画)兼容**:游戏进入分屏再点击桌面图标启动,会重启游戏页面,而并没有完整的启动应用,这会导致部分功能异常, 为了兼容这种情况,SDK 为应用默认设置了`android:resizeableActivity="false"`,即不支持分屏模式, 若游戏出于某些原因必须要覆盖此项,则务必按最低标准设置入口 Activity 和主 Activity的`android:configChanges`,即 ```xml ``` - *SDK 页面*,**方向支持横屏或竖屏,但不支持横竖屏切换** 通过`OperateConfig.setOrientation()`设置 SDK 页面方向,值是系统类`android.content.pm.ActivityInfo`定义的常量 | 方向参数 | 含义 | |------------------------------------------------|------------| | `SCREEN_ORIENTATION_LANDSCAPE`,`int`, 0 | 横屏 | | `SCREEN_ORIENTATION_PORTRAIT`, `int`, 1 | 竖屏 | | `SCREEN_ORIENTATION_SENSOR_LANDSCAPE`,`int`, 6 | 横屏,可180度旋转 | | `SCREEN_ORIENTATION_SENSOR_PORTRAIT`,`int`, 7 | 竖屏 | - *第三方页面*,其方向一般使用 SDK 默认设置即可,需要锁定可设置`android:screenOrientation`,如下所示 ```xml ``` #### 悬浮窗 悬浮窗会在用户登录后附着在游戏页面上层,点击后打开对页面展示个人信息、设置,和游戏论坛、官网等入口。 *极少数游戏登录 Activity 并不是游戏主 Activity ,则悬浮窗可能发生异常,建议游戏做相应调整* - 悬浮窗风格 可根据游戏的风格选择合适的悬浮窗样式,并通过设置`OperateConfig.setPopLogoStyle()`设置。 | 样式类型|图示| |-------------------------|----| | `PopLogoStyle.POPLOGOSTYLE_ONE`(默认) |![image](https://sdkftp.4399doc.com/external/operate/res/m4399_ope_pop_logo_normal_one.png)| | `PopLogoStyle.POPLOGOSTYLE_TWO` |![image](https://sdkftp.4399doc.com/external/operate/res/m4399_ope_pop_logo_normal_two.png)| | `PopLogoStyle.POPLOGOSTYLE_THREE` |![image](https://sdkftp.4399doc.com/external/operate/res/m4399_ope_pop_logo_normal_three.png)| * 悬浮窗初始位置 可根据游戏的风格选择合适的悬浮窗样式,并通过设置`OperateConfig.setPopWinPosition()`设置。 |样式类型|位置| |--------|----| |`PopWinPosition.POS_LEFT`(默认)| 屏幕左侧| |`PopWinPosition.POS_RIGHT`|屏幕右侧| |`PopWinPosition.POS_TOP`|屏幕上侧| |`PopWinPosition.POS_BOTTOM`|屏幕下侧| ### 接口调用:账号相关 #### 账号登录(必须) **功能简介:** 需要登录时调用此接口,如果 SDK 已存在有效登录状态,则直接回调,否则将打开登录页面登录。 **调用时机:** 初始化且回调后 ```java if(mOpeCenter.isLogin()) { User user = mOpeCenter.getCurrentAccount(); } else { mOpeCenter.login(MainActivity.this,new OnLoginFinishedListener() { @Override public void onLoginFinished(boolean success,int resultCode,User user) { // 登录结束后的游戏逻辑 } }); } ``` **最佳实践**:SDK 初始化时会检查已有登录状态有效,若有效`OperateCenter.isLogin()`会返回`true`, 游戏直接通过`OperateCenter.getCurrentAccount()`获取用户信息接口即可,而不需要调用登录接口 在登录成功回调的用户信息,即`User`对象(参考 API 详情),包含`state`字段,此值可用于游戏服务端进行 [用户信息二次验证](https://sdkftp.4399doc.com/external/operate/3.16.0/server_api_1.html) #### 查询是否有已登录账号 **功能简介:** 查询 SDK 中是否已有登录状态,适用于游戏启动时查询 **调用时机:** 账号登录后 ```java boolean isLogin = mOpeCenter.isLogin(); ``` #### 获取当前登录账号信息 **功能简介:** 在 SDK 处于登录状态时,可通过该接口获取当前用户的信息。 **调用时机:** 账号登录后 注意:User 中的uid是 32 位整数字符串;若要转换为数值,注意溢出的可能性 ```java User user = mOpeCenter.getCurrentAccount(); ``` #### 设置角色服务器 id **功能简介:** 若游戏有区服,则应在角色进入分服时,通过以下接口设置所在服的 id。 **调用时机:** 账号登录后 __服务器 id 为不超过10位的数字字符串__ ```java mOpeCenter.setServer(SERVER_ID); ``` #### 账号切换(推荐) **功能简介:** 用于在游戏内切换账号 **调用时机:** 账号登录后 ```java mOpeCenter.switchAccount(MainActivity.this,new OnLoginFinishedListener() { @Override public void onLoginFinished(boolean success,int resultCode,User userInfo){ // 用户账号切换结束后的游戏逻辑 } }); ``` 有时账号切换是由于 SDK 内部发起的,切换成功后 SDK 调用`OnInitGlobalListener.onSwitchUserAccountFinished()`通知游戏 #### 账号登出(推荐) **功能简介:** 当需要登出账号时调用,SDK 立即清除本地用户状态、通知服务端,并调用`OnInitGlobalListener.onUserAccountLogout()`通知游戏 **调用时机:** 账号登录后 ```java mOpeCenter.logout(); ``` ### 接口调用:充值(推荐) **功能简介:** 若游戏支持内购,调用此接口将支持用户完成支付流程,4399 充值系统支持在微信、QQ钱包、支付宝、云闪付等 APP 内完成支付 **调用时机:** 账号登录后,用户点击游戏内充值按钮时 - 全局设置是否支持超出金额:`mOpeCenter.setSupportExcess(SUPPORT_EXPRESS)`接口 可选,默认不支持,此接口也可以在单笔订单中设置 > **关于超出金额模式(超额)** > 由于卡类等充值渠道有金额限制,可能出现*充值金额*无法匹配任何金额的情况; > 如果游戏能处理超出*传入金额* 部分的货币,即将多余部分兑换为游戏币发放给用户,则可以设置`true`; > 否则,应设置为`false`或不设置,SDK将直接隐藏无匹配金额的充值渠道。 - 调用充值接口 ```java /* * 充值,直接传递参数,旧版接口 * * money, 充值金额,整型,单位元,1 ~ 50000,必须 * mark, 游戏方订单,必须, * 支持大小写字母、数字、'|'(竖线)、'-'(中划线)、'_'(下划线),最长32位,不可为空,不可重复 * commodity, 商品名,可选,不传时认为商品名是游戏币 */ mOpeCenter.recharge(MainActivity.this, money, mark, commodity, new OnRechargeFinishedListener() { /* 充值操作结果 */ @Override public void onRechargeFinished(boolean success, int resultCode, String msg) { if (success) { // 充值操作成功,根据服务端回调决定是否道具 // 注意:操作成功包含了订单成功、订单处理中两种情况 } else { // 充值失败逻辑,resultCode查看文档结尾 } } }); /* * 充值,使用订单类,新版接口 * * 与直接传递参数几乎一样,只是可以对单笔订单设置是否支持超出金额 */ mOpeCenter.recharge(MainActivity.this, // 充值金额,整数,单位元 new Order(money, mark) // 是否支持超出金额,默认不支持 .supportExcess(false) // 商品名,可选,不传时认为商品名是游戏币 .commodity(COMMODITY_NAME), new OperateCenter.OnRechargeFinishedListener() { @Override public void onRechargeFinished(boolean success, int resultCode, String msg) { } }); ``` ### 接口调用:绑定手机 **功能简介:** 以下接口用于用户在游戏内绑定手机、查询绑定手机状态 **调用时机:** 账号登录后 #### 绑定 ```java /* * 绑定手机 */ mOpeCenter.bindPhone(this,new OpeResultListener() { /* * resultCode:0、绑定成功;1、取消绑定;2、已绑定;3、网络异常或服务器异常; * message:状态说明 */ @Override public void onResult(int code,@Nullable String message){ } }); ``` #### 检查绑定状态 ```java /* * 检查用户绑定手机的状态 */ mOpeCenter.checkBindPhoneState(new OpeResultListener() { /* * resultCode: 0、未绑定手机号;2、已绑定手机号;3、网络异常或服务器异常;6 、账号被踢 * message:状态说明 */ @Override public void onResult(int code,@Nullable String message){ } }); ``` ### 接口调用:其他 #### 退出游戏(必须) **功能简介:** 当用户按“返回键”或者点击游戏内“退出游戏”时,调用,SDK 会弹出对话框挽留,并展示论坛、礼包等游戏内容 **调用时机:** 初始化且回调后,否则弹出的是默认弹窗,仅确认是否退出游戏 ```java mOpeCenter.shouldQuitGame(MainActivity.this,new OnQuitGameListener() { @Override public void onQuitGame(boolean shouldQuit){ // 点击退出对话框上的“退出游戏”时,shouldQuit为true,游戏处理退出业务逻辑 } }); ``` #### 账号认证(3.8.0+) **功能简介:** 游戏可以对需要进行发布内容、领取福利、充值等操作的账号,进行认证,认证方式由 SDK 后台控制,可选方式为实名认证和短信认证中的任意组合 **调用时机:** 账号登录后 ```java mOpeCenter.authenticate(requireActivity(),new OpeResultListener() { @Override public void onResult(int code,@Nullable String message){ // code 提示不同类型的错误,message 提示具体信息 } }); ``` #### 查看SDK版本号 ```java mOpeCenter.getVersion(); ``` ### 更新支持 SDK 对游戏更新有三类支持方式: - 游戏盒内更新 - 接口更新 #### 游戏盒更新(3.16+) **功能简介:** 游戏按自己的需要提示有版本更新,用户点击提示后可跳转到 4399 游戏盒对应更新页面更新 **调用时机:** 初始化且回调后 使用方式: - 游戏按需要选择检查更新时机,**使用自己的方式判断是否有新版本,SDK 没有提供查询接口支持** - 若有新版本可弹窗提示,其中包括跳转 4399 游戏盒更新的入口,假设是一个“立即更新”按钮 - 更新按钮的点击方法里调用 `OerateCenter.upgradeInGameBox` 方法,用户点击时,若已安装游戏盒,将跳转游戏盒,否则将先下载游戏盒 - 注意处理跳转失败的情况 ```java mOpeCenter.upgradeInGameBox(activity, new OpeResultListener() { /* * code:0、成功; * 1、取消更新,如取消安装游戏盒; 2、更新进行中; 3、更新失败,主要是 SDK 内部请求失败; 5、后台开关未开启; 6、SDK未初始化 * message:code 说明文案 */ @Override public void onResult(int code, @Nullable String message) { // 处理跳转结果 } }); ``` #### 接口更新 **功能简介:** 在 4399 平台发布的游戏,在有新版本提交后,调用此接口可以返回对应的更新弹窗,让用户在应用内即可完成更新。 **调用时机:** 初始化且回调后 此功能需要在4399后台提交新版本,并开启相关开关,详情可咨询运营。 更新功能会检查是否有新版本游戏上线,如果有,则显示更新内容、提示用户升级。 按升级内容,可分为全量更新和**增量更新**(只需下载新旧版 APK 文件中的差异部分)。 按开发者接入方式又可分为自动更新(无需操作,默认初始化完成)和自定义界面更新 自定义界面增量更新接入方法详见: [4399运营SDK增量升级说明](https://sdkftp.4399doc.com/external/operate/3.16.0/operate_upgrade_api.html) ### 渠道支持 **功能简介:** 接入 SDK 的游戏,希望在多个渠道投放,并分别统计数据,有两种方式打入渠道标识 **调用时机:** 静态配置,SDK 初始化即读取 - 打包工具自动打包:联系运营使用专用打包工具打入渠道标识,不需要游戏额外处理。 但此方式要求游戏关闭`v2SigningEnabled`校验 ```groovy // 游戏 apk 工程的 build.gradle中 signingConfigs { release { v2SigningEnabled false } } ``` - 代码写入`meta-data`字段:需要运营与开发人员配合完成,但不会有兼容问题 在游戏模块的 `AndroidManifest.xml`中,设置`FTNN_CHANNEL_ID`字段值,作为渠道标识 ```xml ``` ## API 详情 ### User 类方法 `User`类中是一系列`getter`方法 ```java public final class User { /** uid */ public String getUid(); /** 用户名 */ public String getName(); /** 昵称 */ public String getNick(); /** 登录状态 */ public String getState(); /** 绑定的手机号,已脱敏,若已绑定则此字段非空 */ public String getPhone(); } ``` ### 接口 code 列表 | code | 含义 | |-------------|--------------------------------------| | `0` | 成功,通用code | | `1` | 取消,通用code | | `2` | 处理中,通用code | | `3` | 失败,通用code | | `4` | 超时,通用code | | `5` | 中止,通用code | | `16` | 登录成功 | | `17` | 不能取得游戏盒认证(一般是号被封了) | | `18` | 登录取消 | | `19` | 游戏不存在,GameKey问题 | | `20` | 已登录 | | `21` | 没有初始化 | | `22` | 一键登录失败 | | `24` | 拒绝用户协议与隐私政策 | | `25` | 网络问题,或者服务端问题 | | `50` | 认证通过 | | `51` | 待认证 | | `52` | 拒绝认证 | | `53` | 跳过认证,此时后台配置允许取消 | | `54` | 认证审核中 | | `55` | 取消认证 | | `56` | 认证中断,一般由于账号被顶,或踢出 | | `57` | 认证错误,具体消息看对应message | | `6001` | 充值取消 | | `0`, `9000` | 充值成功 | | `2`, `9001` | 订单处理中,游戏应等待服务端的充值回调状态 | | `9002` | 订单已提交,SDK 不能在有限时间内判断订单状态,游戏应以服务端状态为准 |