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

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

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

理解 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 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 }

其主要問題在于它返回一個(gè)泛型的 T 類型,但沒有在方法簽名中強(qiáng)制指定 T 的具體類型,從而可能導(dǎo)致運(yùn)行時(shí)類型轉(zhuǎn)換錯(cuò)誤。為了提高類型安全性并減少潛在的運(yùn)行時(shí)異常,新的 API 引入了 Class 參數(shù),明確指定期望返回的 Parcelable 對(duì)象的類型。

新的 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 }

這個(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 clazz) 方法。關(guān)鍵在于如何為 C# 類型提供對(duì)應(yīng)的 Java Class 對(duì)象。在 Xamarin.Android 中,我們可以利用 Java.Lang.Class.FromType() 方法來完成這個(gè)轉(zhuǎn)換。

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é)

  1. 類型匹配: 確保 Java.Lang.Class.FromType() 中傳入的類型與實(shí)際存儲(chǔ)在 Bundle 中的 Parcelable 對(duì)象類型一致。
  2. 空值檢查: 無論使用新舊方法,從 Bundle 中取出的對(duì)象都可能為 null(例如,如果鍵不存在或存儲(chǔ)的值就是 null),因此進(jìn)行空值檢查是良好的編程習(xí)慣。
  3. as 運(yùn)算符 即使使用了類型安全的 GetParcelable 方法,返回的仍然是泛型 T,在 C# 中為了將其賦值給具體的 User 類型變量,使用 as User 進(jìn)行類型轉(zhuǎn)換依然是必要的,這有助于在轉(zhuǎn)換失敗時(shí)返回 null 而不是拋出異常。
  4. 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。
  5. 前瞻性: 盡早采納新的 API 標(biāo)準(zhǔn),可以避免未來 Android 版本更新可能帶來的兼容性問題,并使代碼更加健壯和易于維護(hù)。

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

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊6 分享