android API 33 (Tiramisu) 起,Bundle.GetParcelable(String) 方法已被廢棄,推薦使用類型安全的 GetParcelable(string, class
理解 Bundle.GetParcelable 的廢棄與類型安全
在 Android 開發(fā)中,Bundle 是一個(gè)常用的數(shù)據(jù)容器,用于在組件之間(如 Activity 之間)傳遞數(shù)據(jù)。傳統(tǒng)上,我們使用 Bundle.GetParcelable(string key) 方法來檢索存儲(chǔ)的 Parcelable 對(duì)象。然而,從 Android API 33 (Tiramisu) 開始,此方法已被標(biāo)記為廢棄(@Deprecated),并建議使用新的、類型更安全的 GetParcelable(String key, Class
原有的 GetParcelable(string key) 方法簽名如下(摘自 Android 源碼):
/* @deprecated Use the type-safer {@link #getParcelable(String, Class)} starting from Android * {@link Build.VERSION_CODES#TIRAMISU}. */ @Deprecated @NULLable public <T extends Parcelable> T getParcelable(@Nullable String key) { // Implementation here }
其主要問題在于它返回一個(gè)泛型的 T 類型,但沒有在方法簽名中強(qiáng)制指定 T 的具體類型,從而可能導(dǎo)致運(yùn)行時(shí)類型轉(zhuǎn)換錯(cuò)誤。為了提高類型安全性并減少潛在的運(yùn)行時(shí)異常,新的 API 引入了 Class
新的 GetParcelable(String key, Class
/** * Returns the value associated with the given key or {@code null} if: * <ul> * <li>No mapping of the desired type exists for the given key. * <li>A {@code null} value is explicitly associated with the key. * <li>The object is not of type {@code clazz}. * </ul> * * @param key a String, or {@code null} * @param clazz The type of the object expected * @return a Parcelable value, or {@code null} */ @SuppressWarnings("unchecked") @Nullable public <T> T getParcelable(@Nullable String key, @NonNull Class<T> clazz) { // Implementation here }
這個(gè)新方法要求調(diào)用者顯式提供預(yù)期的類型信息,從而在編譯時(shí)就能進(jìn)行更嚴(yán)格的類型檢查,避免了不必要的運(yùn)行時(shí)錯(cuò)誤。
Xamarin.Android 中 Parcelable 對(duì)象的傳遞與接收
在 Xamarin.Android 項(xiàng)目中,我們通常會(huì)定義一個(gè) C# 類并使其實(shí)現(xiàn) IParcelable 接口(或通過 [Parcelable] 屬性自動(dòng)生成相關(guān)代碼),以便在 Bundle 中傳遞。
原始(已廢棄)的代碼示例:
假設(shè)我們有一個(gè)名為 User 的 C# 類,它實(shí)現(xiàn)了 IParcelable 接口,并且我們通過 Intent 和 Bundle 在活動(dòng)之間傳遞 User 對(duì)象。
發(fā)送 User 對(duì)象:
// 假設(shè) MyUser 是一個(gè)已填充數(shù)據(jù)的 User 實(shí)例 User MyUser = new User("...", "...", /* ...其他屬性... */); Intent intent = new Intent(this, typeof(Menu)); Bundle bundlee = new Bundle(); bundlee.PutParcelable("MyUser", MyUser); // 將 User 實(shí)例存入 Bundle intent.PutExtra("TheBundle", bundlee); StartActivity(intent);
請(qǐng)注意,PutParcelable 方法并未廢棄,其使用方式保持不變。
接收 User 對(duì)象(舊的、已廢棄的方式):
// 在目標(biāo) Activity 中接收 Bundle Bundle bundlee = Intent.GetBundleExtra("TheBundle"); User MyUser = new User("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""); // 初始化,但這行在實(shí)際使用中通常不是必需的,可以直接賦值 MyUser = bundlee.GetParcelable("MyUser") as User; // 廢棄警告出現(xiàn)在這里
上述代碼中的 bundlee.GetParcelable(“MyUser”) 會(huì)觸發(fā)廢棄警告,因?yàn)樗褂玫氖遣粠?Class 參數(shù)的舊方法。
遷移至類型安全的 GetParcelable 方法
要解決 Bundle.GetParcelable 的廢棄問題,我們需要使用新的 GetParcelable(String key, Class
Java.Lang.Class.FromType() 方法能夠?qū)⒁粋€(gè) .NET System.Type 對(duì)象轉(zhuǎn)換為對(duì)應(yīng)的 Java java.lang.Class 對(duì)象,這正是 GetParcelable 新方法所需要的。
接收 User 對(duì)象(新的、推薦的方式):
// 在目標(biāo) Activity 中接收 Bundle Bundle bundlee = Intent.GetBundleExtra("TheBundle"); if (bundlee != null) { // 使用新的 GetParcelable 方法,通過 Java.Lang.Class.FromType 提供類型信息 // MyUser = bundlee.GetParcelable("MyUser", Java.Lang.Class.FromType(typeof(User))) as User; // 或者,如果 MyUser 實(shí)例已經(jīng)存在(如通過默認(rèn)構(gòu)造函數(shù)創(chuàng)建),也可以使用其類型 // User MyUser = new User(); // 假設(shè) User 有一個(gè)無參構(gòu)造函數(shù) // MyUser = bundlee.GetParcelable("MyUser", Java.Lang.Class.FromType(MyUser.GetType())) as User; // 更簡潔和推薦的做法是直接獲取并賦值 User MyUser = bundlee.GetParcelable("MyUser", Java.Lang.Class.FromType(typeof(User))) as User; if (MyUser != null) { // 成功獲取到 MyUser 對(duì)象,進(jìn)行后續(xù)操作 Console.WriteLine($"User Name: {MyUser.Name}"); // 假設(shè) User 類有 Name 屬性 } else { Console.WriteLine("Failed to retrieve MyUser object or it was null."); } } else { Console.WriteLine("Bundle is null."); }
在上述代碼中,Java.Lang.Class.FromType(typeof(User)) 將 C# 的 User 類型轉(zhuǎn)換為 Java 運(yùn)行時(shí)所需的 Class 對(duì)象。這樣,GetParcelable 方法就能確保返回的對(duì)象是 User 類型或其子類型,從而滿足類型安全的要求。
注意事項(xiàng)與總結(jié)
- 類型匹配: 確保 Java.Lang.Class.FromType() 中傳入的類型與實(shí)際存儲(chǔ)在 Bundle 中的 Parcelable 對(duì)象類型一致。
- 空值檢查: 無論使用新舊方法,從 Bundle 中取出的對(duì)象都可能為 null(例如,如果鍵不存在或存儲(chǔ)的值就是 null),因此進(jìn)行空值檢查是良好的編程習(xí)慣。
- as 運(yùn)算符: 即使使用了類型安全的 GetParcelable 方法,返回的仍然是泛型 T,在 C# 中為了將其賦值給具體的 User 類型變量,使用 as User 進(jìn)行類型轉(zhuǎn)換依然是必要的,這有助于在轉(zhuǎn)換失敗時(shí)返回 null 而不是拋出異常。
- ClassLoader: Android 官方文檔提到,如果期望的值不是 Android 平臺(tái)提供的類,可能需要先調(diào)用 Bundle.SetClassLoader(ClassLoader)。但在 Xamarin.Android 中,對(duì)于自定義的 Parcelable 類,Java.Lang.Class.FromType 通常會(huì)正確處理類加載器的問題,因此在大多數(shù)情況下無需手動(dòng)設(shè)置。但如果遇到類找不到的異常,可以考慮檢查或設(shè)置 ClassLoader。
- 前瞻性: 盡早采納新的 API 標(biāo)準(zhǔn),可以避免未來 Android 版本更新可能帶來的兼容性問題,并使代碼更加健壯和易于維護(hù)。
通過上述遷移步驟,您的 Xamarin.Android 應(yīng)用將能夠兼容 Android API 33 及更高版本,并消除 Bundle.GetParcelable 相關(guān)的廢棄警告,提升代碼的質(zhì)量和前瞻性。