Unity 游戏集成 Google Health Connect 步数权限解决方案
2024-12-28 13:25:56
Unity游戏中获取步数读取权限
问题
在 Unity 游戏中集成 Google Health Connect,以获取用户的步数数据是一项常见的需求。 但当使用插件架构时,会遇到权限获取问题。 例如,Android 端的原生代码通常依赖 ComponentActivity
进行权限请求。 若插件类仅为普通的 Java 类,直接调用 registerForActivityResult
会引发错误,因为此方法属于 Activity
上下文。
原因分析
registerForActivityResult
函数只能在继承自 Activity
的类(比如 AppCompatActivity
、 ComponentActivity
等)中使用。插件作为普通的 Java 类运行时,无法直接使用 Activity 上下文相关的接口,权限请求就难以执行。Unity 项目调用插件代码时,插件代码会运行在后台线程,并不会关联一个 Android 的 Activity
。 因此,在插件中使用 Activity 的上下文会直接抛出异常。
解决方案一:利用 UnityPlayerActivity
这种方式的核心思想是让 UnityPlayerActivity 来承担权限请求的责任。
操作步骤:
- 在你的 Android 插件项目中,创建一个静态的帮助类,例如
PermissionHelper
。 - 该帮助类包含静态方法,用于存储 Activity 实例,接收权限请求,并发送回调给 Unity。
- 在 Unity 应用启动后(或需要进行权限请求之前),从 Unity 调用插件方法来获得当前 UnityPlayerActivity 的 Activity 实例。
- 当需要权限请求时,从你的 Java 代码中,调用 Activity 的 requestPermissions 启动权限请求流程。
- 通过Activity 的回调( onRequestPermissionsResult ),接收授权结果,再利用 Unity 的发送事件(UnitySendMessage)将结果传递回Unity。
Java 代码示例:
// PermissionHelper.java
import android.app.Activity;
import android.content.pm.PackageManager;
import androidx.core.app.ActivityCompat;
import com.unity3d.player.UnityPlayer;
import android.Manifest;
import androidx.health.connect.client.permission.HealthPermission;
import androidx.health.connect.client.records.StepsRecord;
public class PermissionHelper {
private static Activity sActivity;
private static String sUnityCallbackObjectName;
private static int PERMISSION_REQUEST_CODE = 1; //随便定义一个唯一码
private static String UNITY_CALLBACK_METHOD = "OnPermissionResult";
private static final String[] HEALTH_PERMISSIONS = new String[]{
HealthPermission.getReadPermission(StepsRecord.class).toString(),
HealthPermission.getWritePermission(StepsRecord.class).toString(),
};
public static void setActivity(Activity activity){
sActivity = activity;
}
public static void setUnityCallbackObjectName(String unityCallbackObjectName){
sUnityCallbackObjectName = unityCallbackObjectName;
}
public static boolean checkHealthPermissions() {
if (sActivity != null) {
for(String perm : HEALTH_PERMISSIONS)
if (ActivityCompat.checkSelfPermission(sActivity,perm) != PackageManager.PERMISSION_GRANTED)
return false;
return true;
}else{
return false;
}
}
public static void requestHealthPermissions(){
if (sActivity != null){
ActivityCompat.requestPermissions(sActivity, HEALTH_PERMISSIONS, PERMISSION_REQUEST_CODE);
}
}
public static void onActivityRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == PERMISSION_REQUEST_CODE) {
//此处是安卓授权完毕,回调回JAVA层的处理处,将结果通过 Unity 发送事件传给 Unity 侧处理
boolean allGranted = true;
if(grantResults!=null){
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
}else {
allGranted=false;
}
UnityPlayer.UnitySendMessage(sUnityCallbackObjectName, UNITY_CALLBACK_METHOD,allGranted?"true":"false");
}
}
}
Unity 代码示例:
using UnityEngine;
using System;
using UnityEngine.Android;
public class HealthConnectPlugin : MonoBehaviour
{
private static AndroidJavaClass _pluginClass;
public static string CallBackObjectName = "HealthConnectPlugin";
void Awake() {
// 获取 Plugin class
using(var unityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer")){
using(var currentActivity = unityClass.GetStatic<AndroidJavaObject>("currentActivity")){
using (var jc = new AndroidJavaClass("com.xxx.plugin.PermissionHelper"))//包名换成你的实际包名
{
jc.CallStatic("setActivity",currentActivity);
jc.CallStatic("setUnityCallbackObjectName",CallBackObjectName);
}
}
}
}
public void CheckAndRequestHealthPermissions() {
using (var jc = new AndroidJavaClass("com.xxx.plugin.PermissionHelper"))//包名换成你的实际包名
{
if(jc.CallStatic<bool>("checkHealthPermissions")){
Debug.Log("Already all permission Granted.");
} else {
jc.CallStatic("requestHealthPermissions");
}
}
}
//收到 Android 的授权结果后的回调
public void OnPermissionResult(string allPermissionGranted)
{
Debug.Log( "permissionResult:" + allPermissionGranted);
if (allPermissionGranted=="true") {
Debug.Log( "Permission Granted !");
//在这里去调用下一步骤 获取健康数据的 方法。
}else {
Debug.Log( "Permission Denied !");
}
}
}
补充说明:
在 Android 的 activity (一般就是 UnityPlayerActivity) 的 onRequestPermissionsResult 方法内添加:
PermissionHelper.onActivityRequestPermissionsResult(requestCode, permissions, grantResults);
该方法主要是给静态类传递授权回调,并返回给unity侧。
解决方案二:使用第三方 Activity
这种方式比较通用,但复杂性稍微高。其主要原理是,在你的插件中创建或者使用一个自定义的 Activity ,该 Activity 的作用专门负责权限请求和步数数据获取。这种模式下,所有的 Android 上下文处理和调用都由自定义的 Activity 完成,降低了和UnityplayerActivity的耦合度。
操作步骤:
- 定义一个新的 Activity,专门用于权限请求和 Health Connect 功能。
- 使用
ContextCompat.startActivity()
从插件启动此 Activity 。 - 该 Activity 接收权限请求, 处理数据并回调到Unity (同样通过UnitySendMessage)。
这种方法的步骤稍复杂,需要细致地规划 AndroidManifest.xml
,并且注意进程的管理,本文暂不做示例,如有必要再补全此方案示例代码。
需要注意的是,通过定义单独的Activity来启动权限请求流程,会打断 Unity 主线程的生命周期,造成用户界面的瞬间跳转,因此不建议在游戏主要场景频繁请求授权。可以引导用户跳转到专门的健康模块来操作授权。
额外的安全建议:
- 请求最小权限 : 只请求必要权限,避免过度请求。
- 权限检查 : 始终在使用权限相关功能前,检查权限是否已获得。
- 用户说明 : 在请求权限前,提供明确的理由,提高用户信任。
- 错误处理 : 在权限请求失败时,处理并给出用户反馈,避免崩溃或功能失效。