揭秘 .NET 5 和Java 互操作
早早的.NET團隊就立下了.NET和Java互操作的flag,如果你去翻一翻dotnet/runtime庫,絲毫看不出來倉庫內在搞支持。xamarin/java.interop庫一直有Mono和Java互操作的實現,那么100%的實現.NET和Java互操作就是它,這兩篇文章就是和你一起揭秘.NET和Java互操作。 昨天發了 服務器程序的Xamarin-Java.Interop體驗(一),今天繼續第二部分?服務器程序的Xamarin-Java.Interop體驗(二)。 原本以為會比較容易跑起來demo,但其實還是我太單純了。
那么今天來介紹一下單純的在C#中調用Java代碼段的一些解讀。這樣,意味著我們在本文中會直接調用Java的類,而不會在C#中進行繼承、重寫等。
此時需要考慮用到兩個工具:class-parse和generator。
class-parse通過讀取jar包字節碼,推導出每個類的public、protected方法、字段,并以XML的格式輸出。此工具基本上沒有太大問題,可以直接使用;當然了,你不會在C#里用java的Stream API吧,所以可以考慮改一下源碼來手動去掉stream api。
generator通過讀取上述工具生成的XML和部分引用程序集來生成對應的.cs文件。這個工具似乎官方的進度還不夠快,有很多老舊的類名稱、方法都沒有修改(例如JNIEnv、RegisterAttribute、JniHandleOwnership等)需要魔改后才能正式用起來。https://github.com/yang-er/java-interop 這里提供了我自己魔改的結果,不保證運行正確性、與最終發布時的設計的一致性啊~
上述程序運行完了以后,你會獲得一個一串.cs文件,然后編譯之后就可以在你的C#程序里運行了。注意由于截止目前還沒有支持coreclr,請使用TargetFramework = net472編譯,并在linux/macos上用mono運行。另外直接根據rt.jar編譯出來的文件需要進行一些修改(例如讓Java.Lang.Object繼承于Java.Interop.JavaObject,讓Java.Lang.Throwable繼承于Java.Interop.JavaException)
互操作基本方法
generator將對應類的字段、函數,生成對應的JNI調用代碼,C#運行時調用這個函數就會通過JNI訪問Java的對應功能。
每個函數都會翻譯出來四個部分:
一個cb_XXXX的Delegate,用于緩存互操作的時候Java的callback,在繼承和重寫中需要使用。
一個GetXXXXHandler,用于獲取或創建上述callback的委托。
一個n_XXXXX_函數,是提供上述回調類似于C++的方式訪問(函數簽名都是IntPtr、int等基礎值類型),在C#中獲取對應對象并進行調用。
一個對應的函數,會將傳參列表轉換成jvalue*數組,然后通過JniPeerMember緩存的方法信息進行調用。
普通的字段會被生成成為具有getter和setter的屬性
具有getXXX(),setXXX(value)的一對函數也會被翻譯成屬性
Listener、Observer之類的東西則會被翻譯成事件、EventArgs等
抽象類、接口會生成對應的Invoker,如果C#中沒有注冊返回對象實際對應類型,則會使用這些Invoker來提供一個假的C#實現,否則哪來的類來調用Java方法呢(霧)
一些細節和討論
設計是否正確?
是否有必要將get和set對翻譯成屬性?我個人的觀點是:只翻譯成對應的函數,然后提供一個屬性來訪問對應函數。顯然這些get和set也可能被virtual override,而重寫屬性的話代碼就會長得比較丑了。
另外對有些類型的返回處理是否有必要?例如java.lang.String和System.String之間是否有必要每次調用都轉換?數組直接返回JavaArray不也挺好?有必要將java.util.Collection,java.util.Set等翻譯成System.Collections.ICollection嗎?雖然生成的代碼更C#了,但是實際上似乎會比較影響GC和性能吧?我個人持懷疑態度。
IJavaPeerable
目前與Xamarin.Android一個很大的變化是,他們決定廢棄JNIEnv這個不倫不類的類,改為使用JniEnvironment這個進行良好的整理的類。所以類的生成內容都有變化。原來的JniEnv中提供了直接對IntPtr操作的類,現在由JniObjectReference提供對應的方法來復制,整理的更加“干凈”。
在Xamarin團隊決定將互操作支持帶到桌面上的時候,他們一開始使用了SafeHandle來代替原來的IntPtr,但是發現性能下降明顯,所以后期他們全部改成了JniObjectReference。目前的generator大部分還都返回IntPtr+JniHandleOwnership,你需要改成ref JniObjectReference+JniObjectReferenceOptions。
除此之外,與初代實現的不同一點是,
類型系統相容性
顯然Java中,Throwable是繼承于Object的,但是如果想在C#中強類型處理Java異常,Throwable就不能再繼承于Object了,除非之后CLR規范修改(霧)
另外目前的Generator生成出來的并沒有泛型,全部都是平鋪直敘的類。如果想支持C#那樣的泛型,需要后期他們繼續增加支持,目前你需要自己寫一些膠水代碼(繼承、重寫、cast)來“支持”。
另外Java還支持重寫某函數以后返回比父類更具體的子類類型,這一點C#是不支持的,所以你可能需要修改生成的膠水代碼才能編譯。
性能
這套框架走JNI,所以其實性能其實不會太差?但是需要注意的是,這套框架目前翻譯Java數組、CharSequence的時候,會有Java數組內容復制到C#數組,和C#數組內容復制到Java數組里,這兩個過程,你需要非常小心,盡量在膠水中少使用數組,多使用ArrayList等。
完成進度
我怎么總覺得按他們的速度,這個功能會跳票啊?(大霧)
總結
以上是生活随笔為你收集整理的揭秘 .NET 5 和Java 互操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务器程序的Xamarin-Java.I
- 下一篇: Asp.Net Boilerplate微