首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

Android UUID 比較失效真相:對象引用 vs 內容比較的經典陷阱

     編輯:程式設計疑難解答

Android 開發中使用 UUID 做藍牙/設備唯一標識時,明明 Log 打印內容相同,== 比較卻返回 false,.equals() 甚至也可能失效。本文揭示根因並給出正確比較姿勢。

問題現場重現


// 從 Intent/SharedPreferences/藍牙掃描獲取
UUID uuid1 = ...; // 來源 A
UUID uuid2 = ...; // 來源 B

Log.d("TAG", "uuid1: " + uuid1); // 輸出: 00001101-0000-1000-8000-00805F9B34FB
Log.d("TAG", "uuid2: " + uuid2); // 輸出: 00001101-0000-1000-8000-00805F9B34FB

if (uuid1 == uuid2) { ... }        // ❌ false - 比較對象引用
if (uuid1.equals(uuid2)) { ... }   // ✅ 應為 true,但某些情況仍失效?

根因深度解析

1. == 比較的是對象引用(堆內存地址)

  • UUID 是不可變類,每次 UUID.fromString()、Parcel.readSerializable()、反序列化都會創建新對象
  • 即使內容完全相同,堆中也是不同實例 → == 必然 false

2. .equals() 應該可靠,為何仍失效?

UUID.equals(Object obj) 源碼 (OpenJDK/Android):


public boolean equals(Object obj) {
    if (!(obj instanceof UUID)) return false;
    UUID other = (UUID) obj;
    return mostSigBits == other.mostSigBits && leastSigBits == other.leastSigBits;
}
  • 僅比較兩個 long 域 (mostSigBits, leastSigBits)
  • 正常情況下必定可靠

3. 真實失效場景:字符串解析格式差異


// 場景:藍牙掃描獲取的 128-bit UUID 字符串格式不標準
String str1 = "00001101-0000-1000-8000-00805F9B34FB"; // 標準 8-4-4-4-12
String str2 = "0000110100001000800000805F9B34FB";     // 無分隔符
String str3 = "1101";                                   // 16-bit 簡寫

UUID u1 = UUID.fromString(str1); // 正常
UUID u2 = UUID.fromString(str2); // 拋出 IllegalArgumentException!
UUID u3 = UUID.fromString(str3); // 拋出異常!

// 正確處理 16-bit 藍牙 UUID
UUID bluetoothBase = UUID.fromString("00000000-0000-1000-8000-00805F9B34FB");
long uuid16 = 0x1101;
UUID u = new UUID(bluetoothBase.getMostSignificantBits() | (uuid16 << 32), 
                  bluetoothBase.getLeastSignificantBits());

正確比較最佳實踐

方案一:統一轉為標準字符串比較(最穩健)


String canonical1 = uuid1.toString().toUpperCase().replace("-", "");
String canonical2 = uuid2.toString().toUpperCase().replace("-", "");
if (canonical1.equals(canonical2)) { ... }

方案二:比較兩個 long 域(最高效)


if (uuid1.getMostSignificantBits() == uuid2.getMostSignificantBits() &&
    uuid1.getLeastSignificantBits() == uuid2.getLeastSignificantBits()) { ... }

方案三:封裝工具類(推薦項目統一使用)


public final class UuidUtils {
    public static boolean equals(UUID a, UUID b) {
        if (a == b) return true;
        if (a == null || b == null) return false;
        return a.getMostSignificantBits() == b.getMostSignificantBits() &&
               a.getLeastSignificantBits() == b.getLeastSignificantBits();
    }
    
    public static UUID fromBluetooth16Bit(int uuid16) {
        UUID base = UUID.fromString("00000000-0000-1000-8000-00805F9B34FB");
        return new UUID(base.getMostSignificantBits() | ((long)uuid16 << 32),
                        base.getLeastSignificantBits());
    }
    
    public static String toCanonicalString(UUID uuid) {
        return uuid.toString().toUpperCase().replace("-", "");
    }
}

調試技巧

  1. 打印 mostSigBits、leastSigBits 十六進制:Log.d("TAG", String.format("%016X%016X", uuid.getMostSignificantBits(), uuid.getLeastSignificantBits()));
  2. 檢查字符串源頭:藍牙 16-bit/32-bit/128-bit 格式轉換邏輯
  3. 使用 Objects.equals(uuid1, uuid2) 防空指針

核心原則:永不使用 == 比較 UUID,統一規範字符串解析格式,封裝工具類項目級複用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved