1. 介绍
简介
根据国家新闻出版署的最新规定,发布中国大陆的所有网络游戏必须接入国家新闻出版署网络游戏防沉迷实名验证系统,并且仅可在周五、周六、周日和法定节假日每日20时至21时向未成年人提供1小时网络游戏服务,其他时间均不得以任何形式向未成年人提供网络游戏服务,并且游戏付费服务需要对未成年人支付充值金额设限。
华为平台已为您实现了整套防沉迷的控制逻辑,您只需编写少量代码即可实现防沉迷。本codelab针对发布中国大陆的游戏,演示如何通过华为帐号登录游戏,并快速高效实现防沉迷功能。
您将建立什么
在本次codelab中,您将建立一个具备简单游戏交互功能的Android应用程序,您的应用程序将包含:
- 具备游戏初始化按钮,点击按钮后调用游戏初始化接口并注册防沉迷回调,以限制未成年玩家游戏时间。
- 具备华为帐号登录按钮,点击按钮后登录华为帐号。
- 具备获取玩家信息按钮,点击后获取玩家的信息。
您将会学到什么
通过这个Codelab,您将学到:
- 如何在AppGallery Connect上创建应用
- 如何集成华为HMS Core游戏服务。
- 如何在您的应用中实现登录和防沉迷功能。
2. 您需要什么
硬件要求
- 开发计算机(台式机或笔记本电脑),需要安装android代码运行环境。
- 安卓手机(带USB线),用于真机调试开发。
软件要求
- Android Studio 3.6.1及以上
- JDK 1.8 及以上
- miniSdkVersion 19及以上
- targetSdkVersion 31(推荐)
- compileSdkVersion 31 (推荐)
- Gradle 5.4.1及以上
- gradle插件3.5.4以上
需要的知识点
- Android基础开发能力
- Java基础语法
- 参考帐号注册认证注册成为开发者
3. 能力接入准备
集成HUAWEI HMS Core能力,需要完成以下准备工作:
- 创建AppGallery Connect应用,创建的应用类型必须是"游戏"。
- 创建Android Studio工程。
- 生成签名证书。
- 获取签名证书指纹。
- AppGallery Connect网站配置签名证书指纹。
- 添加配置文件。
具体操作,请按照《HMS Core集成准备》中详细说明来完成。
4. 服务开通
- 在AppGallery Connect首页点击"我的项目",在项目列表中选择您创建好的项目,在"项目设置"页面中选择"API管理"。
- 点击 "华为帐号 "和"游戏服务"后面的开关,开启华为帐号和游戏服务。开关打开后,建议等15分钟以后再测试游戏服务相关功能。
5. 集成SDK
添加当前应用的AppGallery Connect配置文件
- 在AppGallery Connect首页点击"我的项目",在项目下面中点击您的应用。
- 在"项目设置"页面中,点击"应用"栏下的"agconnect-services.json"下载配置文件。
- 将"agconnect-services.json"文件拷贝到应用级根目录下。
配置HMS Core SDK的Maven仓地址
Android Studio的代码库配置在Gradle插件7.0以下版本、7.0版本和7.1及以上版本有所不同。请根据您当前的Gradle插件版本,选择对应的配置过程。
7.0以下版本 | 7.0版本 | 7.1及以上版本 |
- 7.0以下版本:
a) 打开Android Studio项目级"build.gradle"文件。
b) 在build.gradle文件的对应位置配置以下内容。- 在"buildscript > repositories"中配置HMS Core SDK的Maven仓地址。
- 在"allprojects > repositories"中配置HMS Core SDK的Maven仓地址。
- 如果App中添加了"agconnect-services.json"文件则需要在"buildscript > dependencies"中增加agcp插件配置。
allprojects { repositories { google() jcenter() // 配置如下地址 maven {url 'https://developer.huawei.com/repo/'} } } buildscript { repositories { google() jcenter() // 配置如下地址 maven {url 'https://developer.huawei.com/repo/'} } dependencies { classpath 'com.android.tools.build:gradle:3.5.4' // 配置如下地址(推荐您使用最新版本的agcp插件) classpath 'com.huawei.agconnect:agcp:1.6.3.300' } }
- 7.0版本:
a) 打开Android Studio项目级"build.gradle"文件。
b) 添加HUAWEI agcp插件以及Maven代码库。- 在"buildscript > repositories"中配置HMS Core SDK的Maven仓地址。
- 如果App中添加了"agconnect-services.json"文件则需要在"buildscript > dependencies"中增加agcp插件配置。
buildscript { repositories { google() jcenter() // 配置如下地址 maven {url 'https://developer.huawei.com/repo/'} } dependencies { classpath 'com.android.tools.build:gradle:3.5.4' // 配置如下地址(推荐您使用最新版本的agcp插件) classpath 'com.huawei.agconnect:agcp:1.6.3.300' } }
dependencyResolutionManagement { ... repositories { google() jcenter() // 配置HMS Core SDK的Maven仓地址。 maven {url 'https://developer.huawei.com/repo/'} } }
- 7.1以上版本:
a) 打开Android Studio项目级"build.gradle"文件。
b) 添加HUAWEI agcp插件以及Maven代码库。
如果App中添加了"agconnect-services.json"文件则需要在"buildscript > dependencies"中增加agcp插件配置。
c) 打开项目级"settings.gradle"文件,配置HMS Core SDK的Maven仓地址。buildscript { dependencies { classpath 'com.android.tools.build:gradle:3.5.4' // 配置如下地址(推荐您使用最新版本的agcp插件) classpath 'com.huawei.agconnect:agcp:1.6.3.300' } }
pluginManagement { repositories { gradlePluginPortal() google() mavenCentral() // 配置HMS Core SDK的Maven仓地址。 maven { url 'https://developer.huawei.com/repo/' } } } dependencyResolutionManagement { ... repositories { google() mavenCentral() // 配置HMS Core SDK的Maven仓地址。 maven { url 'https://developer.huawei.com/repo/' } } }
添加编译依赖
- 打开应用级的"build.gradle"文件。
- 在"dependencies"中添加如下编译依赖。
dependencies { implementation 'com.huawei.hms:hwid:{version}' implementation 'com.huawei.hms:game:{version}' }
- 添加agcp插件配置。请根据实际情况选择:
方式一:在文件头部声明下一行添加如下配置。apply plugin: 'com.huawei.agconnect'
方式二:在plugins中添加如下配置。
plugins { id 'com.android.application' // 添加如下配置 id 'com.huawei.agconnect' }
点击界面上的"Sync Now"同步已完成的配置。
配置混淆脚本
编译APK前需要添加混淆配置,避免功能异常。配置步骤如下:
- 打开Android Studio工程的混淆配置文件proguard-rules.pro。
- 加入混淆配置。
-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.HiAnalyticsInstance{*;} -keep class com.huawei.hms.analytics.HiAnalyticsInstance{*;} -keep class com.huawei.hms.analytics.HiAnalytics{*;}
- 当您启用R8资源缩减(项目级"build.gradle"文件中"shrinkResources"属性为"true")和严格引用检查("res/raw/keep.xml"文件中的"shrinkMode"为"strict")时,请您配置"keep.xml"文件手动保留layout资源,确保应用正常通过华为应用市场上架审核。
<?xml version="1.0" encoding="utf-8"?> <resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@layout/hms_download_progress,@drawable/screen_off,@layout/upsdk*,@drawable/c_buoycircle*,@drawable/hms_game*,@layout/c_buoycircle*,@layout/hms_game*,@strings/hms_game*,@strings/c_buoycircle*" tools:shrinkMode="strict" />
6. 界面设计
您可以参考本Codelab在Android Studio工程中创建如下布局页面。其中,Init按钮点击后可触发初始化接口,SignIn按钮点击后可以触发登录接口,GetGamePlayer按钮用来触发获取玩家信息接口。
<!--布局代码-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/init_btn"
android:layout_width="match_parent"
android:layout_height="@dimen/btn_height"
android:background="@color/btn_background"
android:gravity="center"
android:text="@string/init_text"
android:textColor="@color/text_color"
android:textSize="@dimen/button_size"/>
<TextView
android:id="@+id/login_btn"
android:layout_width="match_parent"
android:layout_height="@dimen/btn_height"
android:background="@color/btn_background"
android:layout_marginTop="@dimen/margin_top"
android:gravity="center"
android:text="@string/login_text"
android:textColor="@color/text_color"
android:textSize="@dimen/button_size" />
<TextView
android:id="@+id/player1_btn"
android:layout_width="match_parent"
android:layout_height="@dimen/btn_height"
android:background="@color/btn_background"
android:gravity="center"
android:layout_marginTop="@dimen/margin_top"
android:text="@string/game_player"
android:textColor="@color/text_color"
android:textSize="@dimen/button_size" />
</LinearLayout>
7. 初始化和防沉迷开发步骤
如果您希望游戏对接国家版署,您可向当地的新闻出版局申请接入网络游戏防沉迷实名认证系统,并获取"bizID(游戏备案识别码)",再将bizID配置到AppGallery Connect,华为会自动对接国家新闻出版署的实名认证系统并开启强制实名认证,无需您进行额外的开发,具体操作请参见版署实名认证申请。
- 在Application的onCreate方法中添加注册Activity的回调监听。
代码如下:
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); HuaweiMobileServicesUtil.setApplication(this); } }
- 在应用启动的第一个Activity中,调用JosAppsClient的init方法初始化游戏SDK,并在init接口传参中注册AntiAddictionCallback防沉迷回调,当满足以下条件时游戏会将结果回调给您,您需要在回调中实现触发防沉迷后的游戏保存、退出等操作。
- 已实名的未成年人在非规定游戏时间内登录游戏,游戏服务会弹框提示玩家不允许游戏,玩家点击"确定"。
- 已实名的未成年人在规定时间内登录游戏,当游戏进行到晚上21时,游戏服务会弹框提示玩家已到游戏时间,玩家点击"知道了"。
代码如下:
public void init() {
AccountAuthParams params = AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM_GAME;
JosAppsClient appsClient = JosApps.getJosAppsClient(this);
// Set the anti-addiction prompt context, this line must be added
// 设置防沉迷提示语的Context
ResourceLoaderUtil.setmContext(this);
Task<Void> initTask = appsClient.init(new AppParams(params, new AntiAddictionCallback() {
@Override
public void onExit() {
// Implement the game addiction prevention function, such as saving the game and invoking the account exit interface.
// 在此处实现游戏防沉迷功能,如保存游戏、调用帐号退出接口
showLog("onExit");
}
}));
initTask.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
showLog("init success");
playersClient = Games.getPlayersClient(MainActivity.this);
// The login interface can be invoked only after the init is successful. Otherwise, error code 7018 is displayed.
// 一定要在init成功后,才可以调用登录接口,否则登录会提示7018错误码
// signIn();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
showLog("init failed, " + e.getMessage());
if (e instanceof ApiException) {
ApiException apiException = (ApiException) e;
int statusCode = apiException.getStatusCode();
// Error code 7401 indicates that the user did not agree to Huawei joint operations privacy agreement
// 错误码为7401时表示用户未同意华为联运隐私协议
if (statusCode == JosStatusCodes.JOS_PRIVACY_PROTOCOL_REJECTED) {
showLog("has reject the protocol");
// You can exit the game or re-call the init interface.
// 在此处实现退出游戏或者重新调用初始化接口
}
// Handle other error codes.
// 在此处实现其他错误码的处理
}
}
});
}
/**
* Log output
* 日志输出
*/
public void showLog(String logLine) {
StringBuffer sbLog = new StringBuffer();
DateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:SS", Locale.ENGLISH);
String time = format.format(new Date());
sbLog.append(time).append(":").append(logLine);
Log.i(TAG, sbLog.toString());
}
8. 登录开发步骤
- 帐号登录接口必须在init回调成功之后调用。
- 调用AccountAuthManager的getService方法初始化AccountAuthService对象,并调用AccountAuthService的silentSignIn方法实现静默登录。如果静默登录失败,调用AccountAuthService的getSignInIntent方法拉起华为帐号授权页面,显式登录结果在onActivityResult里回调。
/** * 游戏登录需要先调用华为帐号认证登录完成之后,再调用getCurrentPlayer或者getGamePlayer接口获取游戏玩家信息 * 1.先调用华为帐号登录接口的静默登录接口,此接口对于已经授权登录过的应用不会再次拉起登录页面。 * 2.静默登录失败一般是由于登录需要授权,此时在回调中调用显示登录接口拉起登录授权页面进行登录认证。 * 登录结果会在onActivityResult回调中获取,可在此时调用游戏获取玩家信息接口。 */ public void signIn() { Task<AuthAccount> authAccountTask = AccountAuthManager.getService(this, getHuaweiIdParams()).silentSignIn(); authAccountTask.addOnSuccessListener( authAccount -> { Toast.makeText(MainActivity.this, "signIn success", Toast.LENGTH_LONG).show(); showLog("signIn success"); }) .addOnFailureListener( new OnFailureListener() { @Override public void onFailure(Exception e) { if (e instanceof ApiException) { ApiException apiException = (ApiException) e; Toast.makeText(MainActivity.this, "signIn failed:" + apiException.getStatusCode(), Toast.LENGTH_LONG).show(); showLog("signIn failed:" + apiException.getStatusCode()); signInNewWay(); } } }); } /** * 获取到华为帐号登录授权页面的Intent,并通过调用startActivityForResult(Intent, int)打开华为帐号登录授权页面。 */ public void signInNewWay() { Intent intent = AccountAuthManager.getService(MainActivity.this, getHuaweiIdParams()).getSignInIntent(); startActivityForResult(intent, SIGN_IN_INTENT); } public AccountAuthParams getHuaweiIdParams() { return new AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM_GAME).createParams(); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == SIGN_IN_INTENT) { if (null == data) { showLog("signIn intent 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"); } else { showLog("signIn failed: " + signInResult.getStatus().getStatusCode()); } } catch (JSONException var7) { showLog("Failed to convert json from signInResult."); } } }
9. 获取玩家信息开发步骤
在华为帐号登录成功后,游戏调用PlayersClient.getGamePlayer()获取玩家信息。
/**
* Obtains user information about gamers.
* 获取游戏玩家用户信息
*/
public void getGamePlayer() {
playersClient = Games.getPlayersClient(this);
Task<Player> playerTask = playersClient.getGamePlayer();
playerTask.addOnSuccessListener(new OnSuccessListener<Player>() {
@Override
public void onSuccess(Player player) {
showLog("getPlayerInfo Success");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// 获取玩家信息失败
if (e instanceof ApiException) {
showLog("getPlayerInfo failed, status: " + ((ApiException) e).getStatusCode());
if (7400 == ((ApiException) e).getStatusCode() || 7018 == ((ApiException) e).getStatusCode()) {
// error code 7400 indicates that the user has not agreed to the joint operations privacy agreement
// error code 7018 indicates that the init API is not called.
// 7400表示用户未签署联运协议,需要继续调用init接口
// 7018表示初始化失败,需要继续调用init接口
init();
}else if (GamesStatusCodes.GAME_STATE_NETWORK_ERROR == ((ApiException) e).getStatusCode()) {
//Error code 7002 indicates that the network is abnormal. You can prompt the player to check the network.
// 错误码7002表示网络异常,此处您可提示玩家检查网络
showLog( "Network error");
}
}
}
});
}
10. 打包测试
- 测试游戏初始化功能:
点击Init按钮,控制台查看打印日志,在Verbose日志中搜索关键字"Game_codeLab",打印"init success"即初始化成功。 - 测试华为帐号登录功能:
点击SignIn按钮,华为帐号授权登录成功之后,看到欢迎提示弹窗,如下图;或在Verbose日志中搜索关键字"Game_codeLab,打印"signIn success."即登录成功。 - 测试获取玩家信息功能:
点击getGamePlayer或者getCurrentPlayer按钮,在Verbose日志中搜索关键字"Game_codeLab",打印"getPlayerInfo success",即获取玩家信息成功。 - 测试防沉迷功能:
- 不在周五、周六、周日和法定节假日每日20时至21时时间段,用实名未成年华为帐号,完成上述3步后,弹出提示框,点击页面上确定按钮,在Verbose日志中搜索关键字"Game_codeLab",打印"onExit",说明防沉迷功能生效。
- 在周五、周六、周日和法定节假日每日20时至21时时间段,用实名未成年华为帐号,完成上述3步后,成功进入游戏,并在超出时间段后弹出如下弹窗,点击弹窗上"知道了"按钮,在Verbose日志中搜索关键字"Game_codeLab",打印"onExit",也说明防沉迷生效。
- 不在周五、周六、周日和法定节假日每日20时至21时时间段,用实名未成年华为帐号,完成上述3步后,弹出提示框,点击页面上确定按钮,在Verbose日志中搜索关键字"Game_codeLab",打印"onExit",说明防沉迷功能生效。
11. 恭喜您
祝贺您,您已经成功完成了Codelab,并学到了:
- 如何在AppGallery Connect上创建应用
- 如何使用华为HMS Core提供的游戏服务
- 如何使用华为游戏服务开发游戏启动、帐号登录、获取玩家信息、防沉迷功能