android API 33 (Tiramisu) 起,Bundle.GetParcelable(String) 方法已被廢棄,推薦使用類型安全的 GetParcelable(string, class
理解 Bundle.GetParcelable 的廢棄與類型安全
在 Android 開發中,Bundle 是一個常用的數據容器,用于在組件之間(如 Activity 之間)傳遞數據。傳統上,我們使用 Bundle.GetParcelable(string key) 方法來檢索存儲的 Parcelable 對象。然而,從 Android API 33 (Tiramisu) 開始,此方法已被標記為廢棄(@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 }
其主要問題在于它返回一個泛型的 T 類型,但沒有在方法簽名中強制指定 T 的具體類型,從而可能導致運行時類型轉換錯誤。為了提高類型安全性并減少潛在的運行時異常,新的 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 }
這個新方法要求調用者顯式提供預期的類型信息,從而在編譯時就能進行更嚴格的類型檢查,避免了不必要的運行時錯誤。
Xamarin.Android 中 Parcelable 對象的傳遞與接收
在 Xamarin.Android 項目中,我們通常會定義一個 C# 類并使其實現 IParcelable 接口(或通過 [Parcelable] 屬性自動生成相關代碼),以便在 Bundle 中傳遞。
原始(已廢棄)的代碼示例:
假設我們有一個名為 User 的 C# 類,它實現了 IParcelable 接口,并且我們通過 Intent 和 Bundle 在活動之間傳遞 User 對象。
發送 User 對象:
// 假設 MyUser 是一個已填充數據的 User 實例 User MyUser = new User("...", "...", /* ...其他屬性... */); Intent intent = new Intent(this, typeof(Menu)); Bundle bundlee = new Bundle(); bundlee.PutParcelable("MyUser", MyUser); // 將 User 實例存入 Bundle intent.PutExtra("TheBundle", bundlee); StartActivity(intent);
請注意,PutParcelable 方法并未廢棄,其使用方式保持不變。
接收 User 對象(舊的、已廢棄的方式):
// 在目標 Activity 中接收 Bundle Bundle bundlee = Intent.GetBundleExtra("TheBundle"); User MyUser = new User("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", ""); // 初始化,但這行在實際使用中通常不是必需的,可以直接賦值 MyUser = bundlee.GetParcelable("MyUser") as User; // 廢棄警告出現在這里
上述代碼中的 bundlee.GetParcelable(“MyUser”) 會觸發廢棄警告,因為它使用的是不帶 Class 參數的舊方法。
遷移至類型安全的 GetParcelable 方法
要解決 Bundle.GetParcelable 的廢棄問題,我們需要使用新的 GetParcelable(String key, Class
Java.Lang.Class.FromType() 方法能夠將一個 .NET System.Type 對象轉換為對應的 Java java.lang.Class 對象,這正是 GetParcelable 新方法所需要的。
接收 User 對象(新的、推薦的方式):
// 在目標 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 實例已經存在(如通過默認構造函數創建),也可以使用其類型 // User MyUser = new User(); // 假設 User 有一個無參構造函數 // 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 對象,進行后續操作 Console.WriteLine($"User Name: {MyUser.Name}"); // 假設 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 類型轉換為 Java 運行時所需的 Class 對象。這樣,GetParcelable 方法就能確保返回的對象是 User 類型或其子類型,從而滿足類型安全的要求。
注意事項與總結
- 類型匹配: 確保 Java.Lang.Class.FromType() 中傳入的類型與實際存儲在 Bundle 中的 Parcelable 對象類型一致。
- 空值檢查: 無論使用新舊方法,從 Bundle 中取出的對象都可能為 null(例如,如果鍵不存在或存儲的值就是 null),因此進行空值檢查是良好的編程習慣。
- as 運算符: 即使使用了類型安全的 GetParcelable 方法,返回的仍然是泛型 T,在 C# 中為了將其賦值給具體的 User 類型變量,使用 as User 進行類型轉換依然是必要的,這有助于在轉換失敗時返回 null 而不是拋出異常。
- ClassLoader: Android 官方文檔提到,如果期望的值不是 Android 平臺提供的類,可能需要先調用 Bundle.SetClassLoader(ClassLoader)。但在 Xamarin.Android 中,對于自定義的 Parcelable 類,Java.Lang.Class.FromType 通常會正確處理類加載器的問題,因此在大多數情況下無需手動設置。但如果遇到類找不到的異常,可以考慮檢查或設置 ClassLoader。
- 前瞻性: 盡早采納新的 API 標準,可以避免未來 Android 版本更新可能帶來的兼容性問題,并使代碼更加健壯和易于維護。
通過上述遷移步驟,您的 Xamarin.Android 應用將能夠兼容 Android API 33 及更高版本,并消除 Bundle.GetParcelable 相關的廢棄警告,提升代碼的質量和前瞻性。