游戏服务是华为向开发者提供的能够快速开发游戏应用的服务。通过华为游戏服务,开发者可以使用用户的华为帐号登录游戏,从而迅速推广游戏,共享华为庞大的用户价值。
在本次Codelab中,您将建立一个能够具备简单游戏交互功能的Android应用程序,您的应用程序将包含:
集成HUAWEI HMS Core能力,需要完成以下准备工作
具体操作,请按照《HMS Core集成准备》中详细说明来完成。
针对Android Studio开发环境,华为提供了Maven仓集成方式的HMS Core SDK包,开发游戏前需集成SDK到您的Android Studio项目中。
allprojects {
repositories {
google()
jcenter()
//配置如下地址
maven {url 'https://developer.huawei.com/repo/'}
}
}
buildscript {
repositories {
google()
jcenter()
//配置如下地址
maven {url 'https://developer.huawei.com/repo/'}
}
}
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.4.2'
//配置如下地址
classpath 'com.huawei.agconnect:agcp:1.4.1.300'
}
}
//配置如下地址
apply plugin: 'com.huawei.agconnect'
dependencies {
//配置如下地址
implementation 'com.huawei.hms:hwid:{version}'
implementation 'com.huawei.hms:game:{version}'
}
-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
-keep class com.huawei.gamebox.plugin.gameservice.**{*;}
-keep interface com.huawei.hms.analytics.type.HAEventType{*;}
-keep interface com.huawei.hms.analytics.type.HAParamType{*;}
-keep class com.huawei.hms.analytics.HiAnalyticsTools{
public static void enableLog();
public static void enableLog(int);
}
-keep class com.huawei.hms.analytics.HiAnalyticsInstance{*;}
-keep class com.huawei.hms.analytics.HiAnalytics{*;}
游戏应用与华为游戏服务交互的功能主要涉及游戏登录和游戏防沉迷,本次Codelab中您可以在您的Android Studio工程中创建一个布局页面,参照下图进行UI设计。
步骤1 引入游戏相关类。
import com.huawei.hmf.tasks.OnFailureListener;
import com.huawei.hmf.tasks.OnSuccessListener;
import com.huawei.hmf.tasks.Task;
import com.huawei.hms.common.ApiException;
import com.huawei.hms.jos.JosApps;
import com.huawei.hms.jos.JosAppsClient;
import com.huawei.hms.jos.games.Games;
import com.huawei.hms.jos.games.PlayersClient;
import com.huawei.hms.jos.games.buoy.BuoyClient;
import com.huawei.hms.jos.games.player.Player;
import com.huawei.hms.jos.games.player.PlayerExtraInfo;
import com.huawei.hms.support.hwid.HuaweiIdAuthManager;
import com.huawei.hms.support.hwid.request.HuaweiIdAuthParams;
import com.huawei.hms.support.hwid.request.HuaweiIdAuthParamsHelper;
import com.huawei.hms.support.hwid.result.AuthHuaweiId;
import com.huawei.hms.support.hwid.service.HuaweiIdAuthService;
步骤2 在Application的onCreate方法中添加注册Activity的回调监听。
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
HuaweiMobileServicesUtil.setApplication(this);
}
@Override
public void onTerminate() {
super.onTerminate();
}
}
步骤3在应用启动的第一个Activity中,调用JosAppsClient的init方法初始化游戏SDK。
/**
* SDK初始化
*/
private void init() {
JosAppsClient appsClient = JosApps.getJosAppsClient(this);
appsClient.init();
showLog("init success");
}
步骤4 调用HuaweiIdAuthManager的getService方法初始化HuaweiIdAuthService对象,并调用HuaweiIdAuthService的silentSignIn方法实现静默登录。如果静默登录失败,调用HuaweiIdAuthService的getSignInIntent方法显式登录。
/**
* 游戏登录需要先调用华为账号认证登录完成之后,再调用getcurrentPlayer接口获取游戏玩家信息
* 1.先调用华为账号登录接口的静默登录接口,此接口对于已经授权登录过的应用不会再次拉起登录页面。
* 2.静默登录失败一般是由于需要首次登录需要授权,此时在回调中调用显式登录接口拉起登录授权页面进行登录认证。
* 登录接口会在onActivity生命周期中获取,可在此时调用游戏获取玩家信息接口。
*/
public void signIn() {
Task<AuthHuaweiId> authHuaweiIdTask = HuaweiIdAuthManager.getService(this, getHuaweiIdParams()).silentSignIn();
authHuaweiIdTask.addOnSuccessListener(new OnSuccessListener<AuthHuaweiId>() {
@Override
public void onSuccess(AuthHuaweiId authHuaweiId) {
Log.i(TAG, "silentsignIn success");
Log.i(TAG, "display:" + authHuaweiId.getDisplayName());
login();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
ApiException apiException = (ApiException) e;
Log.i(TAG, "signIn failed:" + apiException.getStatusCode());
Log.i(TAG, "start getSignInIntent");
// 显式登录,在生命周期onActivityResult中获取接收登录结果
HuaweiIdAuthService service = HuaweiIdAuthManager.getService(MainActivity.this, getHuaweiIdParams());
startActivityForResult(service.getSignInIntent(), 6013);
}
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 6013) {
if (null == data) {
showLog("signIn inetnt is null");
return;
}
String jsonSignInResult = data.getStringExtra("HUAWEIID_SIGNIN_RESULT");
if (TextUtils.isEmpty(jsonSignInResult)) {
showLog("signIn result is empty");
return;
}
try {
HuaweiIdAuthResult signInResult = new HuaweiIdAuthResult().fromJson(jsonSignInResult);
if (0 == signInResult.getStatus().getStatusCode()) {
showLog("signIn success.");
showLog("signIn result: " + signInResult.toJson());
} else {
showLog("signIn failed: " + signInResult.getStatus().getStatusCode());
}
} catch (JSONException var7) {
showLog("Failed to convert json from signInResult.");
}
}
}
public HuaweiIdAuthParams getHuaweiIdParams() {
return new HuaweiIdAuthParamsHelper(HuaweiIdAuthParams.DEFAULT_AUTH_REQUEST_PARAM_GAME).setIdToken().createParams();
}
步骤5 获取玩家信息。
/**
* 获取游戏玩家用户信息
*/
public void login() {
playersClient = Games.getPlayersClient(this);
Task<Player> playerTask = playersClient.getCurrentPlayer();
playerTask.addOnSuccessListener(new OnSuccessListener<Player>() {
@Override
public void onSuccess(Player player) {
playerID = player.getPlayerId();
Log.i(TAG, "getPlayerInfo Success, player info: " + player.getPlayerId());
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// 获取玩家信息失败
if (e instanceof ApiException) {
Log.e(TAG, "getPlayerInfo failed, status: " + ((ApiException) e).getStatusCode());
}
}
});
}
步骤1 进入游戏,游戏开始事件上报。
/**
* 防沉迷事件开始时间上报。
*/
private void timeReportStart() {
if (playersClient == null) {
Log.i(TAG, "playersClient is null, please init playersClient first");
login();
return;
}
if (playerID == null) {
Log.i(TAG, "playerID is null, please getcurrentPlayer login first");
login();
return;
}
String uid = UUID.randomUUID().toString();
Task<String> task = playersClient.submitPlayerEvent(playerID, uid, "GAMEBEGIN");
task.addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String jsonRequest) {
try {
JSONObject data = new JSONObject(jsonRequest);
sessionId = data.getString("transactionId");
} catch (JSONException e) {
showLog("parse jsonArray meet json exception");
return;
}
showLog("submitPlayerEvent traceId: " + jsonRequest);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
String result = "rtnCode:" + ((ApiException) e).getStatusCode();
showLog(result);
}
}
});
}
步骤2 当游戏结束或暂停时上报结束事件。
/**
* 防沉迷事件结束时间上报
* sessionId: 每次Begin上报"开始"事件成功后会在回调中返回此次上报事件的标识,用于"结束"事件的上报闭环统计此段游戏时长。
*/
private void timeReportEnd() {
if (playersClient == null) {
Log.i(TAG, "playersClient is null, please init playersClient first");
login();
return;
}
if (playerID == null) {
Log.i(TAG, "playerID is null, please getcurrentPlayer login first");
login();
return;
}
if (sessionId == null) {
Log.i(TAG, "sessionId is null, please submitPlayerEvent Begin first");
login();
return;
}
Task<String> task = playersClient.submitPlayerEvent(playerID, sessionId, "GAMEEND");
task.addOnSuccessListener(new OnSuccessListener<String>() {
@Override
public void onSuccess(String s) {
showLog("submitPlayerEvent traceId: " + s);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
String result = "rtnCode:" + ((ApiException) e).getStatusCode();
showLog(result);
}
}
});
}
步骤3 游戏定期调用PlayersClient.getPlayerExtraInfo方法查询玩家附加信息。服务器允许的最高频率为10分钟查询一次,一般建议15分钟查询一次。
/**
* 查询防沉迷事件上报详情
* 如果玩家未成年,则需使用PlayerExtraInfo.getPlayerDuration查看其当前的累计游戏时长,该游戏 时长为玩家当天最新的累计游戏时长。
* 游戏需要根据累计游戏时长进行未成年玩家的防沉迷监控,具体规则由开发者自行定义。
*/
private void getPlayerExfra() {
if (playersClient == null) {
Log.i(TAG, "playersClient is null, please init playersClient first");
login();
return;
}
if (sessionId == null) {
Log.i(TAG, "sessionId is null, please submitPlayerEvent Begin first");
login();
return;
}
Task<PlayerExtraInfo> task = playersClient.getPlayerExtraInfo(sessionId);
task.addOnSuccessListener(new OnSuccessListener<PlayerExtraInfo>() {
@Override
public void onSuccess(PlayerExtraInfo extra) {
if (extra != null) {
showLog("IsRealName: " + extra.getIsRealName() + ", IsAdult: " + extra.getIsAdult()
+ ", PlayerId: " + extra.getPlayerId() + ", PlayerDuration: " + extra.getPlayerDuration());
} else {
showLog("Player extra info is empty.");
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
String result = "rtnCode:" + ((ApiException) e).getStatusCode();
showLog(result);
}
}
});
}
在应用首页中依次点击"Init"、"SignIn"和"GetCurrentPlayer",检查是否能够运行成功。
在应用首页中依次点击"Init"、"SignIn"、"GetCurrentPlayer"和"SubmitPlayerEvent_Begin",检查是否能够运行成功。
点击"SubmitPlayerEvent_End"、"GetPlayerExtraInfo",测试防沉迷其他功能,所有接口均有输出结果日志。
Tag:Game_codeLab
祝贺您,您已经成功地构建了您的第一个游戏应用程序,并学到了:
游戏服务的相关API介绍
本Codelab中所用demo源码下载地址如下: