# 修订记录
| 日期 | 版本 | 说明 | 作者 |
| :--: | :--: | :-- | :--: |
| 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);
```