Unity 之 Android 【获取设备的序列号 (Serial Number)/Android_ID】功能的简单封装

news2024/9/26 5:24:41

Unity 之 Android 【获取设备的序列号 (Serial Number)/Android_ID】功能的简单封装

目录

Unity 之 Android 【获取设备的序列号 (Serial Number)/Android_ID】功能的简单封装

一、简单介绍

二、获取设备的序列号 (Serial Number) 实现原理

1、Android

2、 Unity

三、注意实现

四、案例实现简单步骤

五、关键代码


一、简单介绍

Unity 是一个功能强大的跨平台游戏引擎,广泛用于开发视频游戏和其他实时3D互动内容,如模拟器和虚拟现实应用。

游戏引擎:

  •     Unity:Unity Technologies 开发的跨平台游戏引擎,支持2D和3D图形、物理引擎、音频、视频、网络和多平台发布。
  •     跨平台支持:Unity 支持在多个平台上发布,包括 Windows、macOS、Linux、iOS、Android、WebGL、PlayStation、Xbox、Switch 等。

开发环境:

  •     Unity Editor:用于创建和管理 Unity 项目的集成开发环境(IDE)。开发者可以在其中创建场景、设计关卡、编写代码和调试游戏。
  •     场景(Scene):Unity 中的基本构建块,一个场景可以被视为一个关卡或一个游戏中的独立部分。

编程语言:

  •     C#:主要编程语言。Unity 使用 C# 脚本来控制游戏对象和实现游戏逻辑。
  •     UnityScript(已弃用):曾经支持的 JavaScript 变种,但已经被弃用。

Unity 进阶开发涉及更复杂的技术和更深入的知识,以创建高性能、复杂和专业的游戏和应用程序。以下是一些 Unity 进阶开发的关键领域和技术:

设计模式:

  •     学习和应用常见的设计模式(如单例模式、工厂模式、观察者模式)以便更好地组织和管理代码。

异步编程:

  •     使用 C# 的 async 和 await 关键字进行异步编程,以提高应用的响应速度和性能。

依赖注入:

  •     使用依赖注入(如 Zenject)来管理对象的依赖关系,减少耦合,提高代码的可测试性和可维护性。

二、获取设备的序列号 (Serial Number) 实现原理

1、Android

在 Android 10 及以上版本,由于隐私和安全原因,获取设备的序列号 (SN) 变得更加受限。

在 Android 10 及以上版本,可以使用 Build.getSerial() 方法来获取设备的序列号。不过,这需要获取 READ_PRIVILEGED_PHONE_STATE 权限,该权限仅限于系统应用。因此,对于普通应用,可以使用 Build.SERIAL 作为替代,但在 Android 10 上,这个常量已经被弃用并会返回一个占位符值。

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    String serial = Build.getSerial();
} else {
    String serial = Build.SERIAL;
}

需要注意的是,在 Android 9(API 28)及以下版本,Build.SERIAL 可以直接使用。在 Android 10 及以上版本,需要系统权限。

2、 Unity

在 Unity 中开发 Android 应用时,获取设备的序列号(SN)也受到 Android 10 及以上版本隐私和安全策略的影响。以下是如何在 Unity 中实现这些功能的方法:

对于 Android 10 及以上版本,获取设备序列号需要使用 Build.getSerial() 方法,并且需要特定的权限。由于 Unity 使用 C# 代码,你需要通过调用 Java 方法来实现这一点。

1)首先,需要在 AndroidManifest.xml 文件中声明权限:

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />

2)然后,使用 Unity 的 AndroidJavaObject 类来调用 Java 代码:

using UnityEngine;

public class DeviceInfo : MonoBehaviour
{
    public string GetSerialNumber()
    {
        string serialNumber = "Unknown";

        if (Application.platform == RuntimePlatform.Android)
        {
            try
            {
                using (AndroidJavaClass buildClass = new AndroidJavaClass("android.os.Build"))
                {
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
                    {
                        using (AndroidJavaObject buildObject = buildClass.CallStatic<AndroidJavaObject>("getSerial"))
                        {
                            serialNumber = buildObject.Call<string>("toString");
                        }
                    }
                    else
                    {
                        serialNumber = buildClass.GetStatic<string>("SERIAL");
                    }
                }
            }
            catch (System.Exception e)
            {
                Debug.LogError("Error getting serial number: " + e.Message);
            }
        }

        return serialNumber;
    }
}

三、注意实现

  1. 权限管理:确保在运行时请求必要的权限,特别是在 Android 6.0(API 23)及以上版本中,动态权限请求是必需的。
  2. 兼容性检查:由于不同 Android 版本的行为可能不同,代码中需要进行版本检查。
  3. 隐私合规:获取设备的敏感信息时,请确保应用符合相关的隐私政策和法律法规,通知用户并获得必要的同意。

通过上述步骤,你应该能够在 Unity 中成功获取 Android 10 及以上版本设备的序列号。

四、案例实现简单步骤

1、创建 Unity 工程

2、新建脚本 DeviceInfo ,编写代码获取设备的序列号

3、由于这个可能需要权限申请,编写代码申请权限

4、实现申请电话权限,获取设备序列号(TestDeviceInfo.cs)

5、把脚本 TestDeviceInfo.cs 挂载到场景中

6、在 Player Settings 中的 Build 中勾选 Custom Main Manifest ,添加相关 Phone 权限申请

7、打包运行,可能报如下错误

Exception getting serial number: java.lang.SecurityException: getSerial: The uid 10170 does not meet the requirements to access device identifiers.

错误表明尝试获取设备序列号时遇到了权限问题。在 Android 10(API level 29)及以上版本中,获取设备的序列号需要特殊权限,而这些权限通常不适用于普通应用程序。使用 Settings.Secure.ANDROID_ID 是一种更合适的替代方案,因为它不需要这些特殊权限。

8、下面是如何正确使用 Settings.Secure.ANDROID_ID 来获取设备的 Android ID:

    /// <summary>
    /// 获取设备的 Android ID
    /// </summary>
    /// <returns></returns>
    public static string GetAndroidId()
    {
        string deviceId = string.Empty;

#if UNITY_ANDROID && !UNITY_EDITOR
        try
        {
            using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
            {
                AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

                using (AndroidJavaObject contentResolver = currentActivity.Call<AndroidJavaObject>("getContentResolver"))
                {
                    using (AndroidJavaClass secure = new AndroidJavaClass("android.provider.Settings$Secure"))
                    {
                        deviceId = secure.CallStatic<string>("getString", contentResolver, "android_id");
                    }
                }
            }
        }
        catch (AndroidJavaException e)
        {
            Debug.LogError("Exception getting Android ID: " + e.Message);
        }
#endif

        return deviceId;
    }

9、打包运行,能获取到 设备的 Android ID

10、获取设备的 Android ID的 注意事项

  • 唯一性

    • ANDROID_ID 在设备重置时可能会更改,因此不能作为硬件唯一标识符使用,但对于大多数应用场景,它足够可靠。
  • 权限

    • 由于不涉及敏感权限,你不需要在 AndroidManifest.xml 中添加任何额外权限。

五、关键代码

1、DeviceInfo


using UnityEngine;

public class DeviceInfo 
{

    /// <summary>
    /// 获取设备序列号
    /// 需要系统权限
    /// </summary>
    public static string GetSerialNumber()
    {
        string serialNumber = string.Empty;

#if UNITY_ANDROID && !UNITY_EDITOR
        try
        {
            using (AndroidJavaClass buildClass = new AndroidJavaClass("android.os.Build"))
            {
                if (GetSDKInt() >= 26) // Build.VERSION_CODES.O == 26
                {
                    serialNumber = buildClass.CallStatic<string>("getSerial");
                }
                else
                {
                    serialNumber = buildClass.GetStatic<string>("SERIAL");
                }
            }
        }
        catch (AndroidJavaException e)
        {
            Debug.LogError("Exception getting serial number: " + e.Message);
        }
#endif

        return serialNumber;
    }

    /// <summary>
    /// 获取版本
    /// </summary>
    /// <returns></returns>
    private static int GetSDKInt()
    {
        int sdkInt = 0;

#if UNITY_ANDROID && !UNITY_EDITOR
        using (AndroidJavaClass versionClass = new AndroidJavaClass("android.os.Build$VERSION"))
        {
            sdkInt = versionClass.GetStatic<int>("SDK_INT");
        }
#endif

        return sdkInt;
    }

    /// <summary>
    /// 获取设备的 Android ID
    /// </summary>
    /// <returns></returns>
    public static string GetAndroidId()
    {
        string deviceId = string.Empty;

#if UNITY_ANDROID && !UNITY_EDITOR
        try
        {
            using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
            {
                AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");

                using (AndroidJavaObject contentResolver = currentActivity.Call<AndroidJavaObject>("getContentResolver"))
                {
                    using (AndroidJavaClass secure = new AndroidJavaClass("android.provider.Settings$Secure"))
                    {
                        deviceId = secure.CallStatic<string>("getString", contentResolver, "android_id");
                    }
                }
            }
        }
        catch (AndroidJavaException e)
        {
            Debug.LogError("Exception getting Android ID: " + e.Message);
        }
#endif

        return deviceId;
    }
}

2、PermissionWrapper


using UnityEngine;
using UnityEngine.Android;

public class PermissionWrapper : MonoSingleton<PermissionWrapper>
{
    /// <summary>
    /// 请求权限
    /// </summary>
    /// <param name="permission">权限名称</param>
    /// <param name="callback">权限回调:true 权限获取成功回调,false 权限获取失败回调</param>
    public void RequestPermission(string permission, System.Action<string, bool> callback)
    {
        if (Permission.HasUserAuthorizedPermission(permission))
        {
            callback?.Invoke(permission, true);
        }
        else
        {
            StartCoroutine(RequestAndCheckPermission(permission, callback));
        }
    }

    /// <summary>
    /// 协程请求权限
    /// </summary>
    /// <param name="permission">权限名称</param>
    /// <param name="callback">权限回调:true 权限获取成功回调,false 权限获取失败回调</param>
    /// <returns></returns>
    private System.Collections.IEnumerator RequestAndCheckPermission(string permission, System.Action<string, bool> callback)
    {
        Permission.RequestUserPermission(permission);
        yield return new WaitForSeconds(1.0f);

        bool granted = Permission.HasUserAuthorizedPermission(permission);
        callback?.Invoke(permission, granted);
    }
}

3、TestDeviceInfo


using UnityEngine;

public class TestDeviceInfo : MonoBehaviour
{
    /// <summary>
    /// TAG 
    /// </summary>
    const string TAG = "[TestDeviceInfo] ";

    // Start is called before the first frame update
    void Start()
    {

        GetSerialNumber();
    }

    private void GetSerialNumber()
    {
        string permission = "android.permission.READ_PHONE_STATE";
        string sn = string.Empty;
        PermissionWrapper.Instance.RequestPermission(permission, (permission, isGranted) => {
            if (isGranted)
            {
                //sn = DeviceInfo.GetSerialNumber();
                sn = DeviceInfo.GetAndroidId();
            }
            else
            {
                sn = "unknown";
            }

            Debug.Log(TAG + "GetAndroidId() = " + sn);
        });

    }
}

4、AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- 获取 sn 的权限 Start-->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/> 
    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
    <!-- 获取 sn 的权限 End-->
    
    <application>
        <activity android:name="com.unity3d.player.UnityPlayerActivity"
                  android:theme="@style/UnityThemeSelector">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>
    </application>
</manifest>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1705703.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

notepad++ 模糊替换规则

AUTO_INCREMENT\d AUTO_INCREMENT0 ALTER TABLE .* AUTO_INCREMENT0;

计算机网络——在地址栏输入网址(URL)之后都发生了什么

网址&#xff0c;也叫域名&#xff0c;域名就像一个 IP 地址的可读版本&#xff0c;比如&#xff0c;百度的域名 www.baidu.com&#xff0c;他的 ip 是 110.242.68.3&#xff0c;输入 IP 一样可以跳转到百度搜索的页面&#xff0c;我想没有一个人没去记百度的 IP 吧。其实我们真…

Docker 快速更改容器的重启策略(Restart Policies)以及重启策略详解

目录 1. 使用 docker update 命令2. 在启动容器时指定重启策略3. 在 Docker Compose 文件中指定重启策略4. 总结 官方文档&#xff1a;Start containers automatically 1. 使用 docker update 命令 Docker 提供了 docker update 命令&#xff0c;可以在容器运行时更改其重启策…

Audition 2024 for Mac/Win:音频录制与编辑的卓越之选

随着数字媒体的不断发展&#xff0c;音频内容创作已经成为各行各业中不可或缺的一部分。无论是音乐制作、广播节目、播客录制还是影视配音&#xff0c;都需要高品质的音频录制和编辑工具来实现专业水准的作品。在这个充满竞争的时代&#xff0c;要想在音频创作领域脱颖而出&…

JAVASE总结一

1、 2、引用也可以是成员变量&#xff08;实例变量&#xff09;&#xff0c;也可以是局部变量&#xff1b;引用数据类型&#xff0c;引用&#xff0c; 我们是通过引用去访问JVM堆内存当中的java对象&#xff0c;引用保存了java对象的内存地址&#xff0c;指向了JVM堆内存当中…

java项目启动报错

java项目启动报错&#xff1a;java: java.lang.NoSuchFieldError: Class com.sun.tools.javac.tree.JCTree$JCImport does not have member field ‘com.sun.tools.javac.tree.JCTree qualid’ 原因&#xff1a;编译和运行的版本不一样 点击idea文件 点击项目结构 把这两个版本…

埃及媒体分发投放-新闻媒体通稿发布

埃及商业新闻 大舍传媒近日宣布将在埃及商业新闻领域展开新的媒体分发投放。作为埃及最具影响力的商业新闻平台之一&#xff0c;埃及商业新闻将为大舍传媒提供广阔的市场和受众群体。这一合作意味着大舍传媒将有机会通过埃及商业新闻的平台向埃及的商业精英和投资者传递最新的…

记录一次安装k8s初始化失败

实例化 kubeadm init --configkubeadm.yaml --ignore-preflight-errorsSystemVerification报错 [init] Using Kubernetes version: v1.25.0 [preflight] Running pre-flight checks error execution phase preflight: [preflight] Some fatal errors occurred:[ERROR CRI]: co…

引领智能校对行业的革新者:爱校对

我们很高兴向大家介绍爱校对&#xff0c;这是交互未来&#xff08;北京&#xff09;科技有限公司推出的一款前沿智能校对产品。爱校对的诞生&#xff0c;源自清华大学计算机智能人机交互实验室&#xff0c;结合了最先进的技术与理念&#xff0c;旨在为用户提供高效、精准的智能…

【Chapter5】死锁与饥饿,计算机操作系统教程,第四版,左万利,王英

文章目录 1.1 什么是死锁1.2 死锁的类型1.2.1 竞争资源引起的死锁1.2.2 进程间通信引起的死锁1.2.3 其他原因引起的死锁 1.3 死锁产生必要条件1.4 死锁的处理策略1.5 死锁的预防1.5.1 破坏资源独占条件1.5.2 破坏不可剥夺条件1.5.3 破坏保持申请条件1.5.4 破坏循环等待条件 1.6…

R可视化:另类的箱线图

介绍 方格状态的箱线图 加载R包 knitr::opts_chunk$set(echo TRUE, message FALSE, warning FALSE) library(patternplot) library(png) library(ggplot2) library(gridExtra)rm(list ls()) options(stringsAsFactors F)导入数据 data <- read.csv(system.file(&qu…

记一次Chanakya靶机的渗透测试

Chanakya靶机渗透测试 首先通过主机发现发现到靶机的IP地址为:172.16.10.141 然后使用nmap工具对其进行扫描:nmap -sC -sV -sS -p- 172.16.10.141 发现目标靶机开启了80,22,21等多个端口&#xff0c; 访问80端口,发现是一个普通的页面,点击进入多个界面也没有其他有用的信息,然…

PaliGemma – 谷歌的最新开源视觉语言模型(一)

引言 PaliGemma 是谷歌推出的一款全新视觉语言模型。该模型能够处理图像和文本输入并生成文本输出。谷歌团队发布了三种类型的模型&#xff1a;预训练&#xff08;PT&#xff09;模型、混合&#xff08;Mix&#xff09;模型和微调&#xff08;FT&#xff09;模型&#xff0c;每…

【二叉树】非递归实现前中后序遍历

目录 前言 算法思想 非递归实现前序遍历 过程分析 代码 非递归实现中序遍历 过程分析 代码 非递归实现后序遍历 过程分析 代码 前言 1&#xff09;前序&#xff1a;根 左子树 右子树 2&#xff09;中序&#xff1a;左子树 根 右子树 3&#xff09;后序&#xff1…

Mysql 8.0 主从复制及读写分离搭建记录

前言 搭建参考&#xff1a;搭建Mysql主从复制 为什么要做主从复制&#xff1f; 做数据的热备&#xff0c;作为后备数据库&#xff0c;主数据库服务器故障后&#xff0c;可切换到从数据库继续工作&#xff0c;避免数据丢失。架构的扩展。业务量越来越大&#xff0c;I/O访问频…

PID控制中积分项目的理解,消除稳态误差的作用,表示着过去(PID积分控制)

1&#xff0c;消除稳态误差 积分项目是对于历史误差进行的累积&#xff0c;可以理解&#xff0c;系统的误差累积表示不断的在减少误差&#xff0c;最终消除误差&#xff0c;这个过程需要将误差进行累加&#xff0c;才可以真正知道误差的大小是多少&#xff0c;用最终累加的误差…

C++模板方法模式

文章目录 1. 定义抽象基类&#xff08;Abstract Class&#xff09;2. 实现具体子类&#xff08;Concrete Class&#xff09;3. 使用模板方法模板方法模式的优点模板方法模式的应用场景注意事项实现示例抽象类&#xff08;模板&#xff09;具体实现类客户端代码 总结 模板方法模…

期望薪资25K,新浪微博测试4轮面试,没想到过了。。

一面60min 1、离职原因 2、简单的算法题&#xff0c;就是我会什么让写什么&#xff1a; 冒泡排序&#xff0c;二分查找&#xff08;其实这么简单&#xff0c;我还是在指引下写出来的&#xff0c;自己实在太菜&#xff09; 3、简历问答&#xff08;随机抽几个点问&#xff0…

MySQL数据库语法(二)

一、数据库的创建 创建数据库CRATE DATABASE语法&#xff1a;CREATE DATABASE [IF NOT EXISTS]数据库名;功能&#xff1a;用给定的名字创建一个数据库如果数据库已经存在&#xff0c;发生一个错误。查看创建数据库&#xff1a;SHOW CREATE DATABASE <数据库名>&#xff…

Oracle数据库操作问题汇总

一、简介 Oracle Database&#xff0c;又名Oracle RDBMS&#xff0c;或简称Oracle。是甲骨文公司的一款关系数据库管理系统。它是在数据库领域一直处于领先地位的产品。可以说Oracle数据库系统是世界上流行的关系数据库管理系统&#xff0c;系统可移植性好、使用方便、功能强&…