Xamarin Android API 33+ 中 Bundle.GetParcelable 廢棄問題的解決方案與類型安全遷移指南

Xamarin Android API 33+ 中 Bundle.GetParcelable 廢棄問題的解決方案與類型安全遷移指南

android API 33 (Tiramisu) 起,Bundle.GetParcelable(String) 方法已被廢棄,推薦使用類型安全的 GetParcelable(string, class)。本文旨在為 xamarin.Android 開發者提供詳細的遷移指南,解決在活動間傳遞自定義 Parcelable 對象時遇到的廢棄警告。我們將深入探討新 API 的用法,特別是如何正確地為 C# 類提供 Java Class 對象,確保代碼的兼容性和前瞻性,避免未來版本更新帶來的兼容性問題。

理解 Bundle.GetParcelable 的廢棄與類型安全

在 Android 開發中,Bundle 是一個常用的數據容器,用于在組件之間(如 Activity 之間)傳遞數據。傳統上,我們使用 Bundle.GetParcelable(string key) 方法來檢索存儲的 Parcelable 對象。然而,從 Android API 33 (Tiramisu) 開始,此方法已被標記為廢棄(@Deprecated),并建議使用新的、類型更安全的 GetParcelable(String key, Class clazz) 方法。

原有的 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 參數,明確指定期望返回的 Parcelable 對象的類型。

新的 GetParcelable(String key, Class clazz) 方法簽名及注釋:

/**  * 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 clazz) 方法。關鍵在于如何為 C# 類型提供對應的 Java Class 對象。在 Xamarin.Android 中,我們可以利用 Java.Lang.Class.FromType() 方法來完成這個轉換。

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 類型或其子類型,從而滿足類型安全的要求。

注意事項與總結

  1. 類型匹配: 確保 Java.Lang.Class.FromType() 中傳入的類型與實際存儲在 Bundle 中的 Parcelable 對象類型一致。
  2. 空值檢查: 無論使用新舊方法,從 Bundle 中取出的對象都可能為 null(例如,如果鍵不存在或存儲的值就是 null),因此進行空值檢查是良好的編程習慣。
  3. as 運算符 即使使用了類型安全的 GetParcelable 方法,返回的仍然是泛型 T,在 C# 中為了將其賦值給具體的 User 類型變量,使用 as User 進行類型轉換依然是必要的,這有助于在轉換失敗時返回 null 而不是拋出異常。
  4. ClassLoader: Android 官方文檔提到,如果期望的值不是 Android 平臺提供的類,可能需要先調用 Bundle.SetClassLoader(ClassLoader)。但在 Xamarin.Android 中,對于自定義的 Parcelable 類,Java.Lang.Class.FromType 通常會正確處理類加載器的問題,因此在大多數情況下無需手動設置。但如果遇到類找不到的異常,可以考慮檢查或設置 ClassLoader。
  5. 前瞻性: 盡早采納新的 API 標準,可以避免未來 Android 版本更新可能帶來的兼容性問題,并使代碼更加健壯和易于維護。

通過上述遷移步驟,您的 Xamarin.Android 應用將能夠兼容 Android API 33 及更高版本,并消除 Bundle.GetParcelable 相關的廢棄警告,提升代碼的質量和前瞻性。

? 版權聲明
THE END
喜歡就支持一下吧
點贊6 分享