解決Xamarin.Android繫結第三方庫時型別丟失的問題
現象
今天在做一個第三方庫繫結時,遇到如下情況:

摘取其中一段程式碼如下:
/Users/huangboru/myfile/xamarin_workspace/version4/CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.ActionError.cs(88,88): Error CS0234: The type or namespace name 'IInterfaceDataType' does not exist in the namespace 'Com.Xsj.Crasheye' (are you missing an assembly reference?) (CS0234) (CrashEyeTestPlus)
錯誤程式碼的意思是無法找到IInterfaceDataType型別。
解決過程
為了方便檢視繫結的情況,我新建了一個Android+Studio/">Android Studio專案,新增這個第三方庫,去看這個型別是什麼,看到的情況如下:
package com.xsj.crasheye; import android.content.Context; interface InterfaceDataType { String toJsonLine(); void send(Context var1, NetSender var2, boolean var3); void send(NetSender var1, boolean var2); void save(BaseDataSaver var1); }
查閱 ofollow,noindex">官方文件 ,發現此類問題的可能情況有:


通過分析,我認為這個問題是第4種情況:Java允許一個公開型別去繼承一個非公開的型別,而這在.Net中是不受支援的。由於繫結生成器無法生成對非公開型別的繫結,自然也就無法準確地繫結其公開的子類。為了解決這個問題,我們需要在Metadata.xml檔案中將這個非公開的型別宣告為公開型別。
在我的專案中,我新增的程式碼如下:
<attr path="/api/package[@name='com.xsj.crasheye']/interface[@name='InterfaceDataType']" name="visibility">public</attr> <attr path="/api/package[@name='com.xsj.crasheye']/interface[@name='InterfaceExecutor']" name="visibility">public</attr>
不過問題並沒有完全消除,還有一個error:
/Users/huangboru/myfile/xamarin_workspace/version4/CrashEyeTestPlus/obj/Debug/generated/src/Com.Xsj.Crasheye.ActionTransactionStop.cs(38,38): Error CS0234: The type or namespace name 'EnumTransactionStatus' does not exist in the namespace 'Com.Xsj.Crasheye' (are you missing an assembly reference?) (CS0234) (CrashEyeTestPlus)
我跟進到出現問題的地方:
// Metadata.xml XPath field reference: path="/api/package[@name='com.xsj.crasheye']/class[@name='ActionTransactionStop']/field[@name='status']" [Register ("status")] protected global::Com.Xsj.Crasheye.EnumTransactionStatus Status { get { const string __id = "status.Lcom/xsj/crasheye/EnumTransactionStatus;"; var __v = _members.InstanceFields.GetObjectValue (__id, this); return global::Java.Lang.Object.GetObject<global::Com.Xsj.Crasheye.EnumTransactionStatus> (__v.Handle, JniHandleOwnership.TransferLocalRef); } set { const string __id = "status.Lcom/xsj/crasheye/EnumTransactionStatus;"; IntPtr native_value = global::Android.Runtime.JNIEnv.ToLocalJniHandle (value); try { _members.InstanceFields.SetValue (__id, this, new JniObjectReference (native_value)); } finally { global::Android.Runtime.JNIEnv.DeleteLocalRef (native_value); } } }
對方是一個非公開enum型別,我嘗試像上面一樣公開這個enum,但不可行。
觀察程式碼可知,Xamarin在嘗試繫結一個ActionTransactionStop的status欄位,我到原生專案中檢視這個欄位:
public class ActionTransactionStop extends ActionTransaction implements InterfaceDataType { protected EnumTransactionStatus status; ....... }
可知這是一個protected的欄位,我們應該用不著也不應該訪問這個欄位,於是我直接添加了移除這個欄位繫結的程式碼如下:
<remove-node path="/api/package[@name='com.xsj.crasheye']/class[@name='ActionTransactionStop']/field[@name='status']" />
reBuild,ok!