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;}
}
三、注意實現
- 權限管理:確保在運行時請求必要的權限,特別是在 Android 6.0(API 23)及以上版本中,動態權限請求是必需的。
- 兼容性檢查:由于不同 Android 版本的行為可能不同,代碼中需要進行版本檢查。
- 隱私合規:獲取設備的敏感信息時,請確保應用符合相關的隱私政策和法律法規,通知用戶并獲得必要的同意。
通過上述步驟,你應該能夠在 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_EDITORtry{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);}
#endifreturn 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_EDITORtry{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);}
#endifreturn serialNumber;}/// <summary>/// 獲取版本/// </summary>/// <returns></returns>private static int GetSDKInt(){int sdkInt = 0;#if UNITY_ANDROID && !UNITY_EDITORusing (AndroidJavaClass versionClass = new AndroidJavaClass("android.os.Build$VERSION")){sdkInt = versionClass.GetStatic<int>("SDK_INT");}
#endifreturn sdkInt;}/// <summary>/// 獲取設備的 Android ID/// </summary>/// <returns></returns>public static string GetAndroidId(){string deviceId = string.Empty;#if UNITY_ANDROID && !UNITY_EDITORtry{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);}
#endifreturn 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 updatevoid 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"?>
<manifestxmlns: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>