從 Android 10 開始,應用必須具有 READ_PRIVILEGED_PHONE_STATE 特許權限才能訪問設備的不可重置標識符(包含 IMEI 和序列號)。
而這個權限是系統權限,也就是說一般應用將無法再獲取IMEI 和序列號
受影響的方法包括:
Build
TelephonyManager
如果您的應用沒有該權限,但您仍嘗試查詢不可重置標識符的相關信息,則平臺的響應會因目標 SDK 版本而異:
google也給出了一個解決方案
許多使用場景都不需要不可重置的設備標識符。例如,如果您的應用將不可重置的設備標識符用于廣告跟蹤或用戶分析目的,請為這些特定使用場景使用 Android 廣告 ID。要了解詳情,請參閱唯一標識符的最佳做法。
這里大部分方案對國內無效,比如廣告ID,需要google play的服務,但是國內的手機上都閹割掉了。所以我們只能參考一些可用的方案。
這部分我們一起來看官方唯一標識的建議
國內就不要考慮了,需要依賴google play服務
只對單一應用有效,卸載了就變了,不可取。
MAC 地址具有全局唯一性,無法由用戶重置,在恢復出廠設置后也不會變化。因此,一般不建議使用 MAC 地址進行任何形式的用戶標識。運行 Android 10(API 級別 29)和更高版本的設備會報告不是設備所有者應用的所有應用的隨機化 MAC 地址。
在 Android 6.0(API 級別 23)到 Android 9(API 級別 28)中,無法通過第三方 API 使用 Wi-Fi 和藍牙等本地設備 Mac 地址。WifiInfo.getMacAddress() 方法和 BluetoothAdapter.getDefaultAdapter().getAddress() 方法都返回 02:00:00:00:00:00。
此外,在 Android 6.0 到 Android 9 版本中,您還必須擁有下列權限,才能訪問通過藍牙和 Wi-Fi 掃描獲得的附近外部設備的 MAC 地址:
WifiManager.getScanResults() | ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION |
BluetoothDevice.ACTION_FOUND | ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION |
BluetoothLeScanner.startScan(ScanCallback) | ACCESS_FINE_LOCATION 或 ACCESS_COARSE_LOCATION |
所以,mac是僅次于DeviceId的靠譜的標識,不過android 6.0之后獲取不到了。不過有其他方法完善,見后面。
一堆廢話
也是一堆廢話,要么就是國內無法使用,不過提到了SSAID。
SSAID,即ANDROID_ID(Settings.Secure.ANDROID_ID),在8.0系統迎來改變,具體如下:
對于在 OTA 之前安裝到某個版本 Android 8.0(API 級別 26)的應用,除非在 OTA 后卸載并重新安裝,否則 ANDROID_ID 的值將保持不變。要在 OTA 后在卸載期間保留值,開發者可以使用密鑰/值備份關聯舊值和新值。
對于安裝在運行 Android 8.0 的設備上的應用,ANDROID_ID 的值現在將根據應用簽署密鑰和用戶確定作用域。應用簽署密鑰、用戶和設備的每個組合都具有唯一的 ANDROID_ID 值。因此,在相同設備上運行但具有不同簽署密鑰的應用將不會再看到相同的 Android ID(即使對于同一用戶來說,也是如此)。
只要簽署密鑰相同(并且應用未在 OTA 之前安裝到某個版本的 O),ANDROID_ID 的值在軟件包卸載或重新安裝時就不會發生變化。
即使系統更新導致軟件包簽署密鑰發生變化,ANDROID_ID 的值也不會變化。
可以看到8.0之后ANDROID_ID是與應用簽名關聯的,同簽名的應用共用相同的ANDROID_ID,而且卸載重裝不會變化。
而8.0之前,ANDROID_ID是與設備關聯的,當設備首次啟動時,系統會隨機生成一個64位的數字,并以16進制字符串的形式保存到手機系統中,當手機恢復出廠設置后,Android ID會被重置,這是Android ID與Device ID的主要區別。當然還有其他bug,比如有些廠家獲取為null之類的。
所以,ANDROID_ID是可以考慮的選擇之一,后面細說。
想要一個行為獲取穩定的DeviceId是不可能的,我們需要多個行為結合處理。
首先就是傳統的DeviceId,在Android 10一下還是很穩定的。
在Android 8.0之后,就可以考慮用ANDROID_ID來代替DeviceId了。
Settings.System.getString(BaseApp.getAppContext().getContentResolver(), Settings.Secure.ANDROID_ID);這樣可以做一個版本判斷,低于10.0(或8.0)獲取DeviceId,否則獲取ANDROID_ID
如果上面兩步獲取的還是null,那么可以使用mac地址,但是mac由于6.0之后無法通過WifiInfo.getMacAddress()獲取了,所以我們需要處理一下,代碼如下:
public static String getMac(Context context) {String mac = "";if (context == null) {return mac;}if (Build.VERSION.SDK_INT < 23) {mac = getMacBySystemInterface(context);} else {mac = getMacByJavaAPI();if (TextUtils.isempty(mac)){mac = getMacBySystemInterface(context);}}return mac;}@TargetApi(9) private static String getMacByJavaAPI() {try {Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();while (interfaces.hasMoreElements()) {NetworkInterface netInterface = interfaces.nextElement();if ("wlan0".equals(netInterface.getName()) || "eth0".equals(netInterface.getName())) {byte[] addr = netInterface.getHardwareAddress();if (addr == null || addr.length == 0) {return null;}StringBuilder buf = new StringBuilder();for (byte b : addr) {buf.append(String.format("%02X:", b));}if (buf.length() > 0) {buf.deleteCharAt(buf.length() - 1);}return buf.toString().toLowerCase(Locale.getDefault());}}} catch (Throwable e) {}return null; }private static String getMacBySystemInterface(Context context) {if (context == null) {return "";}try {WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);if (checkPermission(context, Manifest.permission.ACCESS_WIFI_STATE)) {WifiInfo info = wifi.getConnectionInfo();return info.getMacAddress();} else {return "";}} catch (Throwable e) {return "";} }可以看到6.0即23以下直接獲取,否則先通過NetworkInterface獲取,獲取不到再通過原方法獲取。
目前來看這一步還是能穩定獲取的。
兜底行為。因為需要我們手動生成,且每次生成的都不一樣。
UUID.randomUUID().toString()所以必須生成一次保存起來。這樣就有一個問題,如果保存到應用內部存儲,卸載后重裝一定要重新生成,這樣就無法判斷是同一設備了。
所以最好將其保存到外部存儲,保證卸載重裝后還能讀取到上次的值。
這樣一般情況下是最穩定的,除非手動刪除該文件。
所以最好的方案,就是將上面四個方案融合在一起,一個個兜底。目前來看,各手機廠商的指導方案也就這幾個方案。
除了上面的方案,還有移動安全聯盟(信通院牽頭)提供的sdk,可以獲取幾種設備標識符,大部分國內廠商都支持。
不過需要申請使用,還沒測試過。
通過上面分析可以看到,官方確實給出了不少替代方案,但是大部分都由于國內的限制而無法使用。所以國內基本上都是通過依次獲取DeviceId、ANDROID_ID、MAC、UUID的方式來得到一個唯一id,流程大致如下:
#mermaid-svg-XtgtGgE8S0ilzScY {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-XtgtGgE8S0ilzScY .error-icon{fill:#552222;}#mermaid-svg-XtgtGgE8S0ilzScY .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XtgtGgE8S0ilzScY .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-XtgtGgE8S0ilzScY .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XtgtGgE8S0ilzScY .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XtgtGgE8S0ilzScY .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XtgtGgE8S0ilzScY .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XtgtGgE8S0ilzScY .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XtgtGgE8S0ilzScY .marker.cross{stroke:#333333;}#mermaid-svg-XtgtGgE8S0ilzScY svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XtgtGgE8S0ilzScY .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XtgtGgE8S0ilzScY .cluster-label text{fill:#333;}#mermaid-svg-XtgtGgE8S0ilzScY .cluster-label span{color:#333;}#mermaid-svg-XtgtGgE8S0ilzScY .label text,#mermaid-svg-XtgtGgE8S0ilzScY span{fill:#333;color:#333;}#mermaid-svg-XtgtGgE8S0ilzScY .node rect,#mermaid-svg-XtgtGgE8S0ilzScY .node circle,#mermaid-svg-XtgtGgE8S0ilzScY .node ellipse,#mermaid-svg-XtgtGgE8S0ilzScY .node polygon,#mermaid-svg-XtgtGgE8S0ilzScY .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XtgtGgE8S0ilzScY .node .label{text-align:center;}#mermaid-svg-XtgtGgE8S0ilzScY .node.clickable{cursor:pointer;}#mermaid-svg-XtgtGgE8S0ilzScY .arrowheadPath{fill:#333333;}#mermaid-svg-XtgtGgE8S0ilzScY .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XtgtGgE8S0ilzScY .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XtgtGgE8S0ilzScY .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-XtgtGgE8S0ilzScY .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-XtgtGgE8S0ilzScY .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XtgtGgE8S0ilzScY .cluster text{fill:#333;}#mermaid-svg-XtgtGgE8S0ilzScY .cluster span{color:#333;}#mermaid-svg-XtgtGgE8S0ilzScY div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XtgtGgE8S0ilzScY :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 不存在 不存在 不存在 存在 存在 存在 DeviceId? ANDROID_ID? MAC? 生成UUID 返回你可能感興趣:
Android 13發布,一起來看看有哪些新功能
詳細解讀Android中的事件分發機制
本文由 貴州做網站公司 整理發布,部分圖文來源于互聯網,如有侵權,請聯系我們刪除,謝謝!
網絡推廣與網站優化公司(網絡優化與推廣專家)作為數字營銷領域的核心服務提供方,其價值在于通過技術手段與策略規劃幫助企業提升線上曝光度、用戶轉化率及品牌影響力。這...
在當今數字化時代,公司網站已成為企業展示形象、傳遞信息和開展業務的重要平臺。然而,對于許多公司來說,網站建設的價格是一個關鍵考量因素。本文將圍繞“公司網站建設價...
在當今的數字化時代,企業網站已成為企業展示形象、吸引客戶和開展業務的重要平臺。然而,對于許多中小企業來說,高昂的網站建設費用可能會成為其發展的瓶頸。幸運的是,隨...
安徽界首市屬于哪個市 界首在安徽哪里?界首在哪里? 界首,安徽省縣級市,由阜陽市管理,位于安徽省西北部,又稱界溝和小上海。南接臨泉縣、阜陽,東接太和縣,西北與河南省沈丘、丹城交界。因南宋著名將軍劉琦失敗而得名??谷諔馉幤陂g,由于交通堵塞,界首沒有受到日軍的侵犯,上海、南京等城市的商人紛紛遷往界首。一度,人口急劇增加,商人聚集,貿易繁榮,因此被譽為小上海。首領歷史悠久,文化豐富。東漢時期,王莽和...
QQ游戲下載好了怎么安裝不了?這需要一點電腦知識。進入游戲廳文件夾,刪除相關游戲文件夾。如何找到這個游戲的文件夾?先打開游戲,打開游戲,再打開任務管理器。在[應用程序]中找到游戲名稱,點擊右鍵,從菜單中選擇[轉到進程]??吹竭M程名,點擊右邊選擇【屬性】,就會看到這個游戲的文件夾。QQ怎么下載各種軟件并安裝?下載軟件有兩種。1.首先,如果你有二維碼,保存它,然后進入主頁點擊右上方的加號,選擇掃描,掃...
聯想y470怎么切換顯卡?有兩種切換方式,一種是手動切換,即Y470底部有一個銀色開關。還有一種方法是在桌面上點擊右鍵,選擇配置可交換顯卡,直接設置。您可以手動或自動更改圖形卡的用途。建議自動切換。用ATI卡是高性能,用Inter是低性能。聯想Y470筆記本怎樣切換雙顯卡?聯想Y470筆記本集成顯卡轉獨立顯卡的方法:1.聯想Y470自帶物理開關,帶獨立顯卡,是銀白色開關。第一次撥的時候旁邊的燈會亮...