Android 接入文档
接入说明
- Android SDK Demo 下载
- (必须适配 android 11系统版本,否则会影响渠道上架)
- 推荐使用Unity 导出 Android Studio 项目出包方式
- 如游戏首次接入,要做好 Android 手机全面屏适配(常见适配方式一般都是默认全屏展示,顶部刘海或摄像头相关不能影响到游戏相关 UI 功能,否则云测时会影响评级)
- 确保所有调用SDK的接口都放在()调用
- Unity 中调用到 java 中的接口注意需要切换到 UI 线程再调用 SDK 的接口
- 请务必详细查看文档,如有对某些接入部分存在疑问可直接在对接群与技术沟通
- 本文档会不定期更新,如涉及接口变动会在群内同步消息,更多更新内容请查看FastSdk更新日志
- 注意在AndroidManifest.xml中的application标签下增加以下配置:android:resizeableActivity="false"
Maven 仓库配置
project:build.gradle
buildscript {
repositories {
maven {
url 'https://sdk.wdyxgames.com/nexus/repository/sdk-public/'
}
}
}
allprojects {
repositories {
maven {
url 'https://sdk.wdyxgames.com/nexus/repository/sdk-public/'
}
}
}
app:build.gradle
dependencies {
implementation 'com.hoolai.access.open:hoolai-core:1.0.6.0'
}
app:AndroidManifest.xml特殊处理
注意
由于使用打包工具会动态替换apk中的包名,请按照以下规则在接入时对AndroidManifest.xml中的配置进行修改替换,否则会出现无法将渠道包同时安装到一台设备或异常闪退等
()
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.hoolai.access.channel.impl">
<!-- 示例 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- application中有以下三个新增配置,必加(游戏无自定义 Application 时android:name需要配置 HLApplication)-->
<application
android:name="com.hoolai.access.open.fastaccess.HLApplication"
android:allowBackup="false"
android:extractNativeLibs="true"
android:resizeableActivity="false"
android:requestLegacyExternalStorage="true"
android:usesCleartextTraffic="true">
<meta-data
android:name="android.max_aspect"
android:value="2.5"
android:allowBackup="false"
android:requestLegacyExternalStorage="true"/>
<activity
android:name="游戏的主Activity"
android:configChanges="screenLayout|orientation|keyboardHidden|keyboard|fontScale|layoutDirection|density|smallestScreenSize|screenSize|uiMode|navigation|touchscreen|locale|mnc|mcc"
android:launchMode="standard">
...
<!-- copy以下内容到游戏的启动类中,必接 -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="hlscheme" android:host="hlhost"/>
</intent-filter>
<!-- 懂球帝分享配置(选接) -->
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!--game可自定义,与请求参数保持一致即可 -->
<data android:scheme="game" />
</intent-filter>
</activity>
<!-- MAIN_ACTIVITY标签的value=游戏的入口类Activity全路径,否则应用宝渠道登录失败 -->
<!-- 该标签必须加 -->
<meta-data
android:name="MAIN_ACTIVITY"
android:value="游戏的主Activity,如com.demo.GameActivity"/>
<!-- 注意!!!! ${applicationId}需要替换成hlApplicationId -->
<!-- 以下为示例 无需copy -->
<!-- TODO 原配置 -->
<provider
android:name="xx.FileProvider"
android:authorities="${applicationId}.Fileprovider"
... />
<!-- TODO 修改后 -->
<provider
android:name="xx.FileProvider"
android:authorities="hlApplicationId.Fileprovider"
... />
<!-- 以上为示例 无需copy -->
</application>
</manifest>
注意
如集成过三方SDK,AndroidManifest文件中包含${applicationId}的配置会被自动替换成当前APK包的包名,会导致出渠道包时无法再被替换,需要研发人员出包后进行自检
自检流程
1.在Android Studio中打开apk包,找到AndroidManifest.xml文件里的package标签,该标签对应的值即该apk的包名。
2.在此文件中搜索该包名,将所有包含此包名的配置(package="包名"配置除外)复制到工程的AndroidManifest文件中,并全部替换成hlApplicationId,重新打包即可。
3.确保游戏Activity的启动模式设置为 android:launchMode="standard"。
接口文档
Application接入(),注意该配置在 AndroidManifest.xml中需要声明
- 方式一:游戏无自定义Application时使用HLApplication
- 方式二:游戏有自定义Application且继承HLApplication.class时无需处理
- 方式三:游戏有自定义Application且未继承HLApplication.class时需要进行以下调用
- 以上三种配置完成后,请检查AndroidManifest.xml中<application android:name="xxxx.xxxx.XXXApplication"是否配置,此处必须配置
public class YourApplication extends YourBaseApplication {
@Override
public void onCreate() {
super.onCreate();
FastSdk.onApplicationCreate(this);
}
@Override
public void onTerminate() {
super.onTerminate();
FastSdk.onTerminate(this);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
FastSdk.onConfigurationChanged(this, newConfig);
}
@Override
protected void attachBaseContext(Context context) {
super.attachBaseContext(context);
FastSdk.attachBaseContext(this, context);
}
}
SDK功能接入
初始化()
注意
- SDK初始化期间,解决游戏黑屏的建议:准备一张名为"unity_static_splash"的图片,拷贝至res/drawable目录即可
- 游戏启动后首先调用此接口,后才能进行热更/版本检测/游戏初始化/登录等功能调用
- 在未收到初始化回调或收到onInitFailed时,游戏不可进行后续功能调用
- 初始化失败时建议引导用户重新初始化或重启游戏
//游戏启动类(非闪屏类)onCreate 生命周期事件中
//SDK初始化回调
FastSdk.hlSystemListener = new HLSystemListener() {
@Override
public void onInitSuccess(InitResult initResult) {
//初始化成功,回调参数InitResult:gameId,channelId,channel
//gameId 产品 id,登录验证时会需要该值
//channelId 产品下不同渠道的 id 值
//channel 渠道值,如 xiaomi,huawei 等
}
@Override
public void onInitFailed(String reason) {
//建议做弹框提示,可增加重启按钮或退出程序按钮
}
@Override
public void onCustomExit() {
//游戏自定义退出界面 ,实现退出逻辑,否则会出现渠道审核不通过情况
//demo只是示例,请勿直接照搬demo
new AlertDialog.Builder(GameActivity.this)
.setTitle("游戏退出弹窗")
.setMessage("我是游戏的弹窗界面哦")
.setNegativeButton("取消", null)
.setPositiveButton("确定", (dialog, which) -> onExitSuccess(s))
.setCancelable(false).show();
}
@Override
public void onExitSuccess(String result) {
//建议游戏按照下面代码逻辑,退出程序兼容性最好
//否则可能会造成oppop、vivo等渠道退出不彻底,导致再次点击启动应用黑屏
moveTaskToBack(true);
finish();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
android.os.Process.killProcess(android.os.Process.myPid());
}
@Override
public void onUpdate(String data) {
//版本升级(接口预留)
}
};
//SDK账号回调
FastSdk.hlAccountListener = new HLAccountListener() {
@Override
public void onRefreshUser(LoginResult result) {
//小号切换,此回调中之需要游戏验证用户合法性问题,无小号可忽略
//如果游戏不支持内部切换玩家信息,游戏需要先登出,再登录(不需要重新初始化)
}
@Override
public void onLoginSuccess(LoginResult result) {
//登录成功
}
@Override
public void onLoginFailed(String reason) {
//登录失败
}
@Override
public void onLogout(Object... var1) {
//登出
}
};
//SDK支付回调
FastSdk.hlPaymentListener = new HLPaymentListener() {
@Override
public void onPaySuccess(String result) {
//支付成功,仅表示回调状态ok,以 server 端通知发货为准
//注意 result 可能为空字符串
}
@Override
public void onPayFailed(String reason) {
//支付失败,注意 reason 可能为空字符串
}
@Override
public void onQuerySuccess(List<GoodsInfo> list) {
//海外游戏用到,获取商品列表用于展示
}
};
//SDK分享回调(注意某些三方分享是收不到回调的,比如微信分享)
FastSdk.hlShareListener = new HLShareListener() {
@Override
public void onShareSuccess() {
//分享成功
}
@Override
public void onShareFailed(String reason) {
//分享失败
}
};
//注意:调用onCreate之前需要将以上用到的接口先进行实例化
FastSdk.onCreate(this);
InitResult
参数名 | 类型 | 描述 |
---|---|---|
gameId | int | 产品id |
channel | String | 渠道名称 |
channelId | int | 渠道id |
LoginResult
参数名 | 类型 | 描述 |
---|---|---|
uid | long | 用户id |
accessToken | String | 令牌 |
nickName | String | 昵称 |
channelUid | String | 渠道用户id |
channel | String | 渠道名称 |
serverArea | String | 扩展参数 |
GoodsInfo
参数名 | 类型 | 描述 |
---|---|---|
itemId | String | 商品id |
itemName | String | 商品名称 |
itemCount | String | 商品数量 |
itemPrice | String | 商品金额 |
showTag | String | 国内可忽略 |
currency | String | 货币类型 |
登录()
注意
- 调用此方法时确保已经执行“game_init_result”事件报送,更多具体内容请查阅 游戏自定义报送
- 初始化未收到回调或初始化失败时禁止调用登录接口
FastSdk.login();
数据上报()
注意(支付之前必须先进行基础数据上报)
- 上报事件类型(EventType)分为四种:创角、进服、升级、自定义
- 创角、进服、升级事件上报时,roleId、roleName、serverId、serverName参数不能为空且必传,否则影响支付功能!!其他必接数据请查看
//eventType取值:
//创角: EventType.CreateRole
//进服: EventType.EnterServer
//升级: EventType.LevelUp
//前三种是必须上报,下面这种根据运营需求进行自定义事件上报
//自定义点:EventType.CustomerEvent
//设置自定义点位后必须设置setExtendAction("xx"),setPhylum("xx")等
PlayerInfo playerInfo = new PlayerInfo();
playerInfo.setRoleId("32424");//角色唯一标识id,必传
playerInfo.setRoleName("昵称");//必传,String
playerInfo.setRoleLevel("6");//必传,注意字符串必须是数字,比如"123"
playerInfo.setZoneId("1");//必传,没有可传默认值"1",注意字符串中的值必须为数字
playerInfo.setZoneName("华东大区");//必传,没有可传默认值,比如"1区"等
playerInfo.setServerId("1");//必传,没有可传默认值"1",注意字符串中的值必须为数字
playerInfo.setServerName("区名称");//必传,没有可传默认值,比如"1区"等
playerInfo.setBalance("66");//必传,获取不到时传""
playerInfo.setVip("5");//必传
playerInfo.setPartyname("帮派名称");//非必传
//扩展信息,支持String、int、double三种数据类型
playerInfo.addExtra("aaa", "test");
playerInfo.addExtra("bbc", 100);
playerInfo.addExtra("ccc", 1.2);
playerInfo.setClassField("");//事件结果,可不填
//playerInfo.setPhylum("");//扩展数据报送时需要按照提供的事件编号填写,如:1
//playerInfo.setExtendAction("点位名称"); //基础打点不传,自定义打点传
FastSdk.report(EventType.EnterServer, PlayerInfo playerInfo);
注意
- 需要接入CLS网络探测功能的同学注意!!!点击查看规则详情
- 该功能请在SDK初始化之后尽早报送,可以单独报送,也可以跟着其他点位一起上报
- 探测地址可以是域名或IP
- 探测地址中的冒号用@替换,避免格式错误
- 探测地址的key必须使用以下三个字段:gameResourceUrl,gameLoginServerUrl,gameServerUrl
如:playerInfo.setExtra("key:value,gameResourceUrl:http@xxx/open/init, gameServerUrl:tcp@111.222.333.6@8888");
EventType
参数名 | 描述 | 是否必填 |
---|---|---|
EventType.CreateRole | 创建角色 | 是 |
EventType.EnterServer | 游戏进服 | 是 |
EventType.LevelUp | 角色升级 | 是 |
EventType.CustomerEvent | 自定义 | 是 |
PlayerInfo
参数名 | 类型 | 描述 | 是否必填 |
---|---|---|---|
roleId | String | 角色id | 是 |
roleName | String | 角色名称 | 是 |
roleLevel | String | 等级 | 是 |
zoneId | String | 大区id,没有大区传serverId值 | 是 |
zoneName | String | 大区名称,没有大区传serverName值 | 是 |
serverId | String | 服务器id | 是 |
serverName | String | 服务器名称 | 是 |
balance | String | 余额 | 是 |
vip | String | vip等级 | 是 |
partyName | String | 公会名称 | 否 |
appVersion | String | 应用版本号 | 是 |
appResVersion | String | 游戏资源版本号 | 是 |
extendAction | String | 事件名称 | 是 |
roleCreateTime | String | 角色创建时间 | 是 |
phylum | String | 事件id,参考“客户端数据上报”文档 | 是 |
classField | String | 事件结果,ok/fail | 否 |
extra | String | 扩展信息(注意:cls报点用到该字段) | 否 |
登出
FastSdk.logout();
支付(),报送进服或创角之后调用,否则无法拉起支付
PayParams payParams = new PayParams();
payParams.setItemId("item60");//商品ID,非必传
payParams.setItemName("60钻石");//商品名称,非必传
payParams.setAmount(100);//商品金额,必传,单位:分
payParams.setNotifyUrl("");//支付回调地址
//扩展信息,回调时原样返回,因为各渠道回调参数限制不一致。
//回调参数暂时只支持长度255。callBackInfo中不要使用这些符号:“|”、“=”、“+”、“/”。
payParams.setCallbackInfo("支付扩展信息,游戏透传参数");
payParams.setCurrency("CNY");//国内可不传或CNY
FastSdk.pay(payParams);
注意:防抖处理建议
- SDK已做防抖处理,3秒内仅触发首次支付。
- 建议客户端在触发购买事件时弹出遮罩层,防止玩家重复点击。收到支付回调后关闭遮罩,若未收到回调,则设置超时时间,超时后自动关闭遮罩。
- 缓存支付数据并在支付完成后进行 Check 是不必要的。若快速点击不同档位,可能实际支付第一笔却缓存第二笔,导致支付成功但 Check 不通过。支付成功弹框应以 SDK 回调为准。
PayParams
参数名 | 类型 | 描述 | 是否必填 |
---|---|---|---|
itemId | String | 商品id | 是 |
itemName | String | 商品名称 | 是 |
amount | int | 金额,单位:分 | 是 |
count | int | 数量,默认填1 | 是 |
callbackInfo | String | 透传字段 | 否 |
notifyUrl | String | 支付回调地址 | 否 |
currency | String | 货币类型,默认:CNY | 是 |
opt | Map<String, String> | 扩展参数 | 否 |
退出程序
FastSdk.exit();
获取商品列表信息
//注意:国内游戏可不接入此方法
FastSdk.queryGoodsInfo();
生命周期方法()
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
FastSdk.onSaveInstanceState(outState);
}
@Override
protected void onStart() {
super.onStart();
FastSdk.onStart(this);
}
@Override
protected void onResume() {
super.onResume();
FastSdk.onResume(this);
}
@Override
protected void onStop() {
super.onStop();
FastSdk.onStop(this);
}
@Override
protected void onPause() {
super.onPause();
FastSdk.onPause(this);
}
@Override
protected void onRestart() {
super.onRestart();
FastSdk.onRestart(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
FastSdk.onDestroy(this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
FastSdk.onNewIntent(intent);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
FastSdk.onActivityResult(this, requestCode, resultCode, data);
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
FastSdk.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
FastSdk.onConfigurationChanged(this, newConfig, getResources());
}
@Override
public void onBackPressed() {
FastSdk.exit();
}
@Override
public Resources getResources() {
return FastSdk.getResources(super.getResources());
}
//以下为更新内容
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
FastSdk.onRestoreInstanceState(savedInstanceState);
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
FastSdk.onWindowFocusChanged(hasFocus);
}
分享
ShareParams params = new ShareParams();
params.setTitle("标题");
params.setContent("内容");
params.setPicPath("picPath");//本地或网络图片链接
//分享链接(懂球帝固定为:dongqiudi://share/circle/)
params.setShareUrl("http://xx.com");
//微信图片
params.setBmp(bmp);
FastSdk.share(ShareType.WX, ShareType.ChildType.WX_CHAT, params);
ShareType
参数名 | 类型 | 描述 |
---|---|---|
ShareType | QQ分享 | |
WX | ShareType | 微信分享 |
DQD | ShareType | 懂球帝分享 |
ShareType | Facebook分享 | |
SYSTEM | ShareType | 系统分享 |
ShareType.ChildType
参数名 | 类型 | 描述 |
---|---|---|
WX_CHAT | ShareType.ChildType | 微信聊天 |
WX_CIRCLE | ShareType.ChildType | 微信朋友圈 |
WX_FAVORITES | ShareType.ChildType | 微信收藏 |
QQ_TEXT_AND_QQZONE | ShareType.ChildType | 分享文字且同步到到QQ空间 |
QQ_IMG_AND_QQZONE | ShareType.ChildType | 分享图片且同步到到QQ空间 |
QQ_TEXT | ShareType.ChildType | 分享文字 |
QQ_IMG | ShareType.ChildType | 分享图片 |
DQD | ShareType.ChildType | 懂球帝 |
FACEBOOK_LINK | ShareType.ChildType | facebook链接 |
FACEBOOK_IMG | ShareType.ChildType | facebook图片 |
FACEBOOK_VIDEO | ShareType.ChildType | facebook视频 |
SYSTEM_TEXT | ShareType.ChildType | 系统分享文字链接 |
SYSTEM_IMG | ShareType.ChildType | 系统分享图片 |
SYSTEM_FILE | ShareType.ChildType | 系统分享文件 |
ShareParams
参数名 | 类型 | 描述 | 是否必填 |
---|---|---|---|
id | String | 懂球帝id,仅懂球帝 | 是 |
wxType | int | 1:聊天,2:朋友圈,3:收藏 | 否 |
qqType | int | 1:默认,2:纯图 | 否 |
showQQZone | boolean | 是否展示到QQ空间 | 否 |
title | String | 标题 | 否 |
content | String | 内容 | 否 |
picPath | String | 图片url | 否 |
shareUrl | String | 分享链接 | 否 |
callbackUrl | String | 懂球帝回调url,仅懂球帝 | 是 |
bmp | Bitmap | 微信图片,仅微信图片分享 | 是 |
权限申请()
注意
政策要求敏感权限申请时被拒绝后不能申请多次,所以SDK限制申请次数为一次。用户申请权限时,可设置是否显示提示框(默认false),当为true且再次调用时会弹出提示框引导用户进行手动授权。一般拒绝该权限会影响业务功能时显示此弹框。
//单个危险权限申请,返回申请结果
boolean hasPermission = FastSdk.checkPermission(Activity activity,
String permission, boolean showRefuseDialog);
//activity:当前页上下文
//permission:权限名称
//showRefuseDialog:是否显示弹框,默认true,非必传!
boolean hasPermission = FastSdk.checkPermission(activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE, true);
if(hasPermission){
//your code
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
FastSdk.onRequestPermissionsResult(requestCode, permissions, grantResults);
//权限申请结果回调,查看是否被授予
if (requestCode == Math.abs(Manifest.permission.WRITE_EXTERNAL_STORAGE.hashCode())){
if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
Logger.i("权限已被允许");
//your code
}else{
Logger.i("权限已被拒绝");
}
}
}
方舟报送(已废弃)
String action = "game_xxx";
Map<String, Object> hashMap = new HashMap<>();
hashMap.put("xxx", 123);
hashMap.put("ooo", "321");
FastSdk.gameDataReport(action, hashMap);
方舟参数
参数名 | 类型 | 描述 | 是否必填 |
---|---|---|---|
action | String | 事件名称 | 是 |
hashMap | Map<String, Object> | 事件内容 | 是 |
CD-KEY(选接,礼包码兑换)
AccessActivityDataInfo info = new AccessActivityDataInfo();
info.setCode("兑换码");
FastSdk.accessParticipate(AccessActivityType.CD_KEY, info);
CD-KEY参数
参数名 | 类型 | 描述 | 是否必填 |
---|---|---|---|
type | AccessActivityType | 活动类型 | 是 |
info | AccessActivityDataInfo | 兑换码对象 | 是 |
Q&A
初始化失败,出现“非正版应用”提示,如何处理?
签名文件不正确,如果没有签名文件可联系运营同学。
账密登录时没有账号?
账密登录方式没有注册功能,需要运营同学提供账号/密码。
如何测试沙箱环境支付?
和运营同学确认,确保已开启沙箱环境,并确保账号已加入测试白名单。
无法拉起支付?
检查进入游戏时是否报送创角或进服事件。
支付成功不到账?
1.检查提供的支付回调url是否正确。
2.检查支付接口,在支付参数中是否调用了setNotifyUrl(url),如果调用了则会优先回调此url。
渠道包中从游戏内登出时悬浮球不隐藏
游戏未调用Fastsdk.logout(),建议游戏“设置”界面做两个按钮:“切换服务器”、“切换账号”。在点击“切换账号”时调用Fastsdk.logout(),点击“切换服务器”时无需登出直接回到选服界面即可。