# 修订记录 | 日期 | 版本 | 说明 | 作者 | | :--: | :--: | :-- | :--: | | 2021-10-29 | 1.0.0 | 文档建立 | 麦锦培 | | 2021-11-10 | 1.0.1 | 1)SDK开放获取QS_GCP_ID能力
2)增加浮标切换账号回调 | 麦锦培 | # 1.接入准备 ## 服务端文档 - [统一签名方式](http://yydocs.yyxxgame.com/web/#/p/b136c6888dff0b5961ffc595d1842034) - [登录校验接口](http://yydocs.yyxxgame.com/web/#/p/3bdc599ce28f6b1179c8958bfaed0725) - [订单回调发货接口](http://yydocs.yyxxgame.com/web/#/p/56bf9d3805853826e1c19338c1c07aca) - [聊天上报接口](http://yydocs.yyxxgame.com/web/#/p/c6c2e0f90ca7c6bd19204007394c4cee) - [游戏用户行为上报接口](http://yydocs.yyxxgame.com/web/#/p/ddf8c5388de930ea7fd53593c17fbdc9) ## 客户端SDK接入Demo工程 - [Git仓库地址](http://gogs.yyxxgame.com/Client/QSGameSdkDemo) ## 引入SDK配置 - 游戏资源文件名、布局名、布局id名等建议使用规范命名,避免和SDK资源冲突 - Android Gradle Plugin Version : 4.1.3+ - Gradle Version : 6.5+ - 拷贝SDK目录下**`assets/supplierconfig.json`**和**`zlsioh.dat`**到项目的assets中 - 拷贝SDK目录下**`assets/qs_game`**至项目中,并修改**`qs_cfg.properties`**文件的值 ```properties QS_PACKAGE_VERSION=出包时间,如20210101 QS_CAMPAIGN_ID=我方提供的分别标识 QS_APP_ID=我方提供的appid QS_APP_KEY=我方提供的appkey QS_OWN_DEBUG=false(true为测试模式,可以看到接口日志弹窗信息) QS_GCP_ID=非必要,按需填写,SDK不会根据这个值做任何逻辑 QS_ONLINE_ENV=按我方提供的填写即可(固定值) ``` ## 引入SDK - 在应用**`build.gradle`**中**`dependencies`**节点下添加SDK依赖,目前只提供远程依赖,需要离线aar包请联系我方技术 > 在项目级**`build.gradle`**文件下的**`allprojects`**节点下添加仓库的url ```groovy allprojects { repositories { ... mavenCentral() ... } } ``` > 在应用级**`build.gradle`**文件下的**`allprojects`**节点下添加仓库的url ```groovy // 打点组件SDK implementation 'io.github.yyxxgame.sdk:qingshi-gamesdk-analytics:2.0.0-rc1' // 支付组件SDK implementation 'io.github.yyxxgame.sdk:qingshi-gamesdk-billing:2.0.0-rc1' // 登录组件SDK implementation 'io.github.yyxxgame.sdk:qingshi-gamesdk-login:2.0.0-rc1' // 核心组件SDK implementation 'io.github.yyxxgame.sdk:qingshi-gamesdk-core:2.0.0-rc1' ``` ## 配置游戏AndroidManifest.xml - 配置游戏AndroidManifest.xml的首个启动Activity为SDK的WelcomeActivity ```xml ``` - 修改AndroidManifest.xml中游戏Activity的**`intent-filter`**属性,假设DemoActivity为游戏的Activity,则需要修改**`action`**标签的**`adnroid:name`**为游戏的包名 ```xml ``` ## 混淆配置 - SDK已经经过混淆,SDK内部个别方法使用到了反射,建议不做混淆,如必须开启混淆,请参考并添加以下混淆配置 ```pro -keep class cn.qingshi.gamesdk.**{*;} # support sdk -keep class cn.yyxx.support.**{*;} # msa sdk -keep class XI.**{*;} -keep class com.asus.**{*;} -keep class com.bun.**{*;} -keep class com.huawei.hms.ads.identifier.**{*;} -keep class com.samsung.android.deviceidservice.**{*;} -keep class com.netease.nis.sdkwrapper.** {*;} -keep class com.zui.**{*;} -keep class org.json.**{*;} # authsdk -keep class com.alibaba.**{*;} -keep class com.cmic.**{*;} -keep class com.mobile.**{*;} -keep class com.nirvana.**{*;} # mmkv -keep class com.tencent.mmkv.**{*;} # zap -keep class com.dolin.zap.**{*;} # v4-support -keep class android.support.**{*;} ``` # 2.接口说明 > **SDK的所有接口如无特殊说明,默认均为必接且请务必在游戏主线程调用** ## 1)同步Application中的生命周期 需要在游戏的Application类的**attachBaseContext()**及**onCreate()**中实现SDK方法 ```java public class DemoApplication extends Application { @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); QSGameSdk.getInstance().attachBaseContext(this, base); } @Override public void onCreate() { super.onCreate(); QSGameSdk.getInstance().initApplication(this); } } ``` ## 2)同步游戏Activity中各个生命周期 - 同步onStart ```java @Override protected void onStart() { super.onStart(); QSGameSdk.getInstance().onStart(this); } ``` - 同步onResume ```java @Override protected void onResume() { super.onResume(); QSGameSdk.getInstance().onResume(this); } ``` - 同步onRestart ```java @Override protected void onRestart() { super.onRestart(); QSGameSdk.getInstance().onReStart(this); } ``` - 同步onPause ```java @Override protected void onPause() { super.onPause(); QSGameSdk.getInstance().onPause(this); } ``` - 同步onStop ```java @Override protected void onStop() { super.onStop(); QSGameSdk.getInstance().onStop(this); } ``` - 同步onDestroy ```java @Override protected void onDestroy() { super.onDestroy(); QSGameSdk.getInstance().onDestroy(this); } ``` - 同步onActivityResult ```java @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); QSGameSdk.getInstance().onActivityResult(this, requestCode, requestCode, data); } ``` - 同步onNewIntent ```java @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); QSGameSdk.getInstance().onNewIntent(this, intent); } ``` ## 3)SDK通用回调接口ICallback说明 ```java public interface ICallback { void onResult(int code, String result); } ``` | 参数 | 类型 | 说明 | | :--: | :--: | :--: | | code | int | 状态码(0成功,-1失败) | | result | string | 返回信息 | ## 4)SDK初始化 - 在游戏的主Activity的onCreate中调用 ```java /** * SDK初始化 * * @param activity Activity上下文 * @param isLandscape 是否横屏 * @param callback SDK初始化回调 */ public void initialize(final Activity activity, final Boolean isLandscape, ICallback callback) ``` - 示例 ```java @Override protected void onCreate(Bundle savedInstanceState){ QSGameSdk.getInstance().initialize(this, false, new ICallback() { @Override public void onResult(int code, String result) { if (code == 0) { //TODO SDK初始化成功 } else { //TODO SDK初始化失败,result是失败的debug信息 } } }); } ``` ## 5)用户账号登录 ```java /** * SDK用户登录 * * @param activity Activity上下文 * @param callback 登录回调对象 */ public void login(Activity activity, ICallback callback) ``` **登录回调返回的result信息** | 参数 | 类型 | 说明 | | :--: | :--: | :--: | | open_id | string | SDK的用户ID | | game_token | string | 签名token | - 示例 ```java QSGameSdk.getInstance().login(this, new ICallback(){ @Override public void onResult(int code, String result) { if (code == 0) { //TODO 对SDK返回的用户信息进行验签 //result返回的是JSON字符串,可以得到open_id和game_token等信息 //签名规则详见服务端接入文档 } else { //TODO 登录失败,result是返回的debug信息 } } }); ``` ## 6)用户账号登出 > 用户在游戏中触发登出,游戏回到选服界面重新拉起登录 ```java /** * SDK用户登出账号 * * @param activity Activity上下文 * @param callback 登出回调对象 */ public void logout(Activity activity, ICallback callback) ``` - 示例 ```java QSGameSdk.getInstance().logout(this, new ICallback() { @Override public void onResult(int code, String result) { if (code == 0) { //TODO 用户登出成功,登出成功后请返回游戏选服界面,然后调用SDK的登录接口拉起登录框 } else { //TODO 用户登出失败,result是返回的debug信息 } } }); ``` ## 7)角色信息上报 > 请务必根据当前角色的触发的事件进行上报 角色信息实体对象QSRoleInfo,如无特别说明所有字段均不能为null或空串"" | 参数 | 类型 | 说明 | | :--: | :--: | :--: | | openId | string | 当前用户ID | | serverNo | string | 当前角色所在的服务器ID | | serverName | string | 当前角色所在的服务器名 | | roleId | string | 当前角色ID | | roleName | string | 当前角色名 | | roleLevel | string | 当前角色等级 | | roleGender | string | 当前角色性别,如无默认传男 | | balance | string | 当前角色游戏币余额,若无传"0" | | power | string | 当前角色战力,若无传"0" | | vipLevel | string | 当前Vip角色等级,若无传"0" | | roleCTime | string | 当前角色创建的时间戳(10位),角色一旦创建则是固定值 | | roleLevelMTime | string | 当前角色升级的时间戳(10位),如果是创角则传创角时间,如果是角色登录则传入上一次角色等级变化的时间 | | ext | string | 扩展参数,不需要则传入空字符串"" | > 角色创建 ```java /** * SDK角色创建信息上报 * * @param activity Activity上下文 * @param roleInfo 角色信息实体 */ public void roleCreate(Activity activity, QSRoleInfo roleInfo) ``` > 角色进入服务器(角色登录) ```java /** * SDK角色登录信息上报 * * @param activity Activity上下文 * @param roleInfo 角色信息实体 */ public void roleLauncher(Activity activity, QSRoleInfo roleInfo) ``` > 角色升级 ```java /** * SDK角色升级信息上报 * * @param activity Activity上下文 * @param roleInfo 角色信息实体 */ public void roleUpgrade(Activity activity, QSRoleInfo roleInfo) ``` - 示例 ```java QSRoleInfo gameRoleInfo = new QSRoleInfo(); // 用户ID gameRoleInfo.setOpenId("用户ID"); // 服务器ID gameRoleInfo.setServerNo("服务器ID"); // 服务器名 gameRoleInfo.setServerName("服务器名"); // 角色ID gameRoleInfo.setRoleId("角色id"); // 角色名称 gameRoleInfo.setRoleName("角色名称"); // 角色等级 gameRoleInfo.setRoleLevel("角色等级"); // 角色性别,无该字段则传"男" gameRoleInfo.setRoleGender("角色性别"); // 当前角色游戏币余额,无该字段则传"0" gameRoleInfo.setBalance("当前角色游戏币余额"); // 当前角色战力,无该字段则传"0" gameRoleInfo.setPower("当前角色战力"); // 用户VIP等级,无该字段则传"0" gameRoleInfo.setVipLevel("用户VIP等级"); // 创角时间戳(角色一旦创角则是固定值) gameRoleInfo.setRoleCTime("创角10位时间戳") // 角色等级变化时间戳 gameRoleInfo.setroleLevelMTime("角色等级变化时间戳") // 扩展字段 gameRoleInfo.setExt("扩展字段"); //角色创建 QSGameSdk.getInstance().roleCreate(this, gameRoleInfo); //角色登录 QSGameSdk.getInstance().roleLauncher(this, gameRoleInfo; //角色升级 QSGameSdk.getInstance().roleUpgrade(this, gameRoleInfo; ``` ## 8)支付储值 > 请务必保证调用角色信息上报中的角色登录上报后再调用 > **客户端SDK回调的只是支付流程的结果,实际支付结果将由服务端回调** 支付信息实体对象QSChargeInfo,如无特别说明所有字段均不能为null或空串"" | 参数 | 类型 | 说明 | | :--: | :--: | :--: | | openId | string | 当前用户ID | | serverNo | string | 当前角色所在的服务器ID | | serverName | string | 当前角色所在的服务器名 | | roleId | string | 当前角色ID | | roleName | string | 当前角色名 | | roleLevel | string | 当前角色等级 | | roleGender | string | 当前角色性别,如无默认传男 | | balance | string | 当前角色游戏币余额,若无传"0" | | power | string | 当前角色战力,若无传"0" | | vipLevel | string | 当前Vip角色等级,若无传"0" | | cpOrderId | string | 游戏订单号 | | productName | string | 商品名称 | | amount | int | 金额,单位分 | | cpCallbackInfo | string | 透传信息,该字段内容会原样在服务端接口返回 | ```java /** * SDK用户支付 * * @param activity Activity上下文 * @param chargeInfo 支付信息实体对象 * @param callback 支付回调对象 */ public void charge(Activity activity, QSChargeInfo chargeInfo,ICallback callback) ``` - 示例 ```java QSChargeInfo gameChargeInfo = new QSChargeInfo(); // 用户ID gameChargeInfo.setOpenId("用户ID"); // 服务器ID gameChargeInfo.setServerNo("服务器ID"); // 服务器名 gameChargeInfo.setServerName("服务器名"); // 角色ID gameChargeInfo.setRoleId("角色id"); // 角色名称 gameChargeInfo.setRoleName("角色名称"); // 角色等级 gameChargeInfo.setRoleLevel("角色等级"); // 角色性别,无该字段则传"男" gameChargeInfo.setRoleGender("角色性别"); // 当前角色游戏币余额,无该字段则传"0" gameChargeInfo.setBalance("当前角色游戏币余额"); // 当前角色战力,无该字段则传"0" gameChargeInfo.setPower("当前角色战力"); // 用户VIP等级,无该字段则传"0" gameChargeInfo.setVipLevel("用户VIP等级"); // 游戏订单号 gameChargeInfo.setCpOrderId("游戏订单号"); // 商品名称 gameChargeInfo.setProductName("商品名称") // 金额 gameChargeInfo.setAmount(0) // 透传字段 gameChargeInfo.setCpCallbackInfo("透传字段"); //客户端SDK回调的只是支付流程的结果,实际支付结果将由服务端回调 QSGameSdk.getInstance().charge(this, gameChargeInfo , new ICallback() { @Override public void onResult(int code, String result) { if (code == 0) { //TODO 支付流程完成 } else { //TODO 支付失败 } } }); ``` ## 10)设置SDK浮标用户账号登出回调 > 建议在SDK初始化成功后设置,该回调会监听用户浮标切换账号的行为,处理逻辑跟logout接口保持一致即可 ```java /** * 浮标退出回调 * * @param activity Activity上下文 * @param callback 浮标退出回调对象 */ public void onFloatCenterLogout(Activity activity, ICallback callback) ``` - 示例 ```java QSGameSdk.getInstance().onFloatCenterLogout(this, new ICallback() { @Override public void onResult(int code, String result) { if (code == 0) { //TODO 用户浮标登出成功,登出成功后请返回游戏选服界面,然后调用SDK的登录接口拉起登录框 } else { //TODO 用户浮标登出失败,result是返回的debug信息 } } }); ``` ## 11)显示SDK退出框(选接) > 用户(玩家)按下返回键时调用,接入方需要实现Activity的onKeyDown,并判断keyCode为KeyEvent.KEYCODE_BACK时调用该接口 ```java /** * 显示退出框 * * @param activity Activity上下文 * @param callback 退出回调对象 */ public void openExitView(Activity activity, ICallback callback) ``` - 示例 ```java //重写Activity的onKeyDown并判断KeyDown事件 @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { QSGameSdk.getInstance().openExitView(this, new ICallback() { @Override public void onResult(int code, String result) { if (code == 0) { // 结束当前Activity // 如果要杀进程需要在onDestroy中处理,不然会不执行SDK的onDestroy方法 finish(); } } }); return true; } return super.onKeyDown(keyCode, event); } @Override protected void onDestroy() { super.onDestroy(); QSGameSdk.getInstance().onDestroy(this); // 结束当前应用进程 System.exit(0); } ``` ## 12)获取SDK当前用户ID(选接) ```java /** * 获取当前SDK用户ID * * @return */ public String getCurrentOpenId() ``` - 示例 ```java QSGameSdk.getInstance().getCurrentOpenId(); ``` ## 13)获取SDK当前版本(选接) ```java /** * 获取当前SDK版本 * * @return */ public String getCurrentSdkVersion() ``` - 示例 ```java QSGameSdk.getInstance().getCurrentSdkVersion(); ``` ## 14)权限请求(选接) > 若游戏需要动态请求特殊或危险权限,可以选择使用SDK的权限请求接口,该功能已经适配Android 12的行为变更 > SDK内部已经申请了SD卡存储权限及获取设备信息权限 ```java /** * 权限请求 * * @param activity Activity上下文 * @param permissions 权限列表 * @param callback 权限请求回调对象 */ public void requestPermission(Activity activity, List permissions, ICallback callback) ``` - 示例 ```java List permissions = new ArrayList() {{ // 相机摄像头 add(Permission.CAMERA); // 音频麦克风 add(Permission.RECORD_AUDIO); }}; QSGameSdk.getInstance().requestPermission(activity, permissions, new ICallback() { @Override public void onResult(int code, String result) { if (code == 0) { //TODO 授权成功,result是成功授权的权限json字符串 } else { //TODO 授权是白,result是失败授权的权限json字符串 } } }); ``` ## 15)日志接口(选接) > 若游戏有需要持久化java、kotlin和C++(联系我方技术获取头文件)层的日志,可以使用SDK的日志接口。SDK的日志框架底层使用C++进行开发,使用了Linux系统的mmap内存拷贝技术 > SDK会默认缓存3天的日志,过期则自动删除 > 缓存的文件使用Facebook开源的压缩算法zlib进行压缩 > 缓存路径为**手机内部存储目录/Android/data/包名/files/dolin/zap**,即应用的私有目录 > 更多使用方法请联系我方技术 - 示例 ```java // debug日志 Logger.d(TAG,msg); // info日志 Logger.i(TAG,msg); // error日志 Logger.e(TAG,msg); ``` ## 16)获取SDK配置文件QS_GCP_ID(选接) > 为了方便接入方需要,这个接口可以获取SDK配置文件中的QS_GCP_ID接入方填写的值,SDK不会根据这个值做任何逻辑 ```java /** * 获取SDK配置文件QS_GCP_ID * * @param context Context上下文 */ public static void getQsGcpId(Context context) ``` - 示例 ```java // debug日志 ParamsUtils.getQsGcpId(context); ```