亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Unity中的原生插件及平臺交互原理是什么

發布時間:2021-12-03 16:44:10 來源:億速云 閱讀:262 作者:柒染 欄目:大數據

這篇文章給大家介紹Unity中的原生插件及平臺交互原理是什么,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。

原生插件/平臺交互

雖然大多時候使用Unity3D進行游戲開發時,只需要使用C#進行邏輯編寫。但有時候不可避免的需要使用和編寫原生插件,例如一些第三方插件只提供C/C++原生插件、復用已有的C/C++模塊等。有一些功能是Unity3D實現不了,必須要調用Android/iOS原生接口,比如獲取手機的硬件信息(UnityEngine.SystemInfo沒有提供的部分)、調用系統的原生彈窗、手機震動等等

1 、C/C++插件

編寫和使用原生插件的幾個關鍵點:

創建C/C++原生插件

  • 導出接口必須是C ABI-compatible函數

  • 函數調用約定

在C#中標識C/C++的函數并調用

  • 標識 DLL 中的函數。至少指定函數的名稱和包含該函數的 DLL 的名稱。

  • 創建用于容納 DLL 函數的類。可以使用現有類,為每一非托管函數創建單獨的類,或者創建包含一組相關的非托管函數的一個類。

  • 在托管代碼中創建原型。使用 DllImportAttribute 標識 DLL 和函數。 用 static 和 extern 修飾符標記方法。

  • 調用 DLL 函數。像處理其他任何托管方法一樣調用托管類上的方法。

在C#中創建回調函數,C/C++調用C#回調函數

  • 創建托管回調函數。

  • 創建一個委托,并將其作為參數傳遞給 C/C++函數。平臺調用會自動將委托轉換為常見的回調格式。

  • 確保在回調函數完成其工作之前,垃圾回收器不會回收委托。

那么C#與原生插件之間是如何實現互相調用的呢?

1.將源碼編譯為托管模塊;

2.將托管模塊組合為程序集;

3.加載公共語言運行時CLR;

4.執行程序集代碼。

注:CLR(公共語言運行時,Common LanguageRuntime)和Java虛擬機一樣也是一個運行時環境,它負責資源管理(內存分配和垃圾收集),并保證應用和底層操作系統之間必要的分離。

為了提高平臺的可靠性,以及為了達到面向事務的電子商務應用所要求的穩定性級別,CLR還要負責其他一些任務,比如監視程序的運行。按照.NET的說法,在CLR監視之下運行的程序屬于"托管"(managed)代碼,而不在CLR之下、直接在裸機上運行的應用或者組件屬于"非托管"(unmanaged)的代碼。

這幾個過程我總結為下圖:
Unity中的原生插件及平臺交互原理是什么
圖.NET上的程序運行

回調函數是托管代碼C#中的定義的函數,對回調函數的調用,實現從非托管C/C++代碼中調用托管C#代碼。那么C/C++是如何調用C#的呢?大致分為2步,可以用下圖表示:
Unity中的原生插件及平臺交互原理是什么

將回調函數指針注冊到非托管C/C++代碼中(C#中回調函數指委托delegate)

調用注冊過的托管C#函數指針

相比較托管調用非托管,回調函數方式稍微復雜一些。回調函數非常適合重復執行的任務、異步調用等情況下使用。

由上面的介紹可以知道CLR提供了C#程序運行的環境,與非托管代碼的C/C++交互調用也由它來完成。CLR提供兩種用于與非托管C/C++代碼進行交互的機制

  • 平臺調用(Platform Invoke,簡稱PInvoke或者P/Invoke),它使托管代碼能夠調用從非托管DLL中導出的函數。

  • COM 互操作,它使托管代碼能夠通過接口與組件對象模型 (COM) 對象交互。考慮跨平臺性,Unity3D不使用這種方式。

平臺調用依賴于元數據在運行時查找導出的函數并封送(Marshal)其參數。下圖顯示了這一過程。
Unity中的原生插件及平臺交互原理是什么
注意:

1. 除涉及回調函數時以外,平臺調用方法調用從托管代碼流向非托管代碼,而絕不會以相反方向流動。雖然平臺調用的調用只能從托管代碼流向非托管代碼,但是數據仍然可以作為輸入參數或輸出參數在兩個方向流動。

2. 圖中DLL表示動態庫,Windows平臺指.dll文件、Linux/Android指.so文件、Mac OS X指.dylib/framework文件、iOS中只能使用.a。后文都使用DLL代指,并且DLL使用C/C++編寫。

當"平臺調用"調用非托管函數時,它將依次執行以下操作:

  • 查找包含該函數的DLL。

  • 將該DLL加載到內存中。

  • 查找函數在內存中的地址并將其參數推到堆棧上,以封送所需的數據(參數)。

  • 將控制權轉移給非托管函數。

注意:  只在第一次調用函數時,才會查找和加載 DLL并查找函數在內存中的地址。iOS中使用的是.a已經靜態打包到最終執行文件中。

2、 Android插件

Java同樣提供了這樣一個擴展機制JNI(Java NativeInterface),能夠與C/C++互相通信。

注:

  • JNI wiki-https://en.wikipedia.org/wiki/Java_Native_Interface 這里不深入介紹JNI,有興趣的可以自行去研究。如果你還不知道JNI也不用怕,就像Unity3D使用C/C++庫一樣,用起來還是比較簡單的,只需要知道這個東西即可。并且Unity3D對C/C++橋接器這塊做了封裝,提供AndroidJNI/AndroidJNIHelper/AndroidJavaObject/AndroidJavaClass/AndroidJavaProxy方便使用等,具體使用后面在介紹。JNI提供了若干的API實現了Java和其他語言的通信(主要是C&C++)。從Java1.1開始,JNI標準成為java平臺的一部分,它允許Java代碼和其他語言寫的代碼進行交互,保證本地代碼能工作在任何Java虛擬機環境下。"

  • 作為知識擴展,提一下Android Java虛擬機。Android的Java虛擬機有2個,最開始是Dalvik,后面Google在Android 4.4系統新增一種應用運行模式ART。ART與Dalvik 之間的主要區別是其具有提前 (AOT) 編譯模式。 根據 AOT 概念,設備安裝應用時,DEX 字節代碼轉換僅進行一次。 相比于 Dalvik,這樣可實現真正的優勢 ,因為 Dalvik 的即時 (JIT) 編譯方法需要在每次運行應用時都進行代碼轉換。下文中用Java虛擬機代指Dalvik/ART。

C#/Java都可以和C/C++通信,那么通過編寫一個C/C++模塊作為橋接,就使得C#與Java通信成為了可能,如下圖所示:
Unity中的原生插件及平臺交互原理是什么
注:C/C++橋接器本身跟Unity3D沒有直接關系,不屬于Android和Unity3D,圖中放在Unity3D中是為了代指libunity.so中實現的橋接器以表示真實的情況。

通過JNI既可以用于Java代碼調用C/C++代碼,也可用于C/C++代碼與Java(Dalvik/ART虛擬機)的交互。JNI定義了2個關鍵概念/結構:JavaVM、JNIENV。JavaVM提供虛擬機創建、銷毀等操作,Java中一個進程可以創建多個虛擬機,但是Android一個進程只能有一個虛擬機。JNIENV是線程相關的,對應的是JavaVM中的當前線程的JNI環境,只有附加(attach)到JavaVM的線程才有JNIENV指針,通過JNIEVN指針可以獲取JNI功能,否則不能夠調用JNI函數。

Unity中的原生插件及平臺交互原理是什么

C/C++要訪問的Java代碼,必須要能訪問到Java虛擬機,獲取虛擬機有2中方法:

  • 在加載動態鏈接庫的時候,JVM會調用JNI_OnLoad(JavaVM* jvm, void* reserved),第一個參數會傳入JavaVM指針。

  • 在C/C++中調用JNI_CreateJavaVM(&jvm, (void**)&env, &vm_args)創建JavaVM指針

所以,我們只需要在編寫C/C++橋接器so的時候定義JNI_OnLoad(JavaVM* jvm, void*reserved)方法即可,然后把JavaVM指針保存起來作為上下文使用。

獲取到JavaVM之后,還不能直接拿到JNI函數去獲取Java代碼,必須通過線程關聯的JNIENV指針去獲取。所以,作為一個好的開發習慣在每次獲取一個線程的JNI相關功能時,先調用AttachCurrentThread();又或者每次通過JavaVM指針獲取當前的JNIENV:java_vm->GetEnv((void**)&jni_env, version),一定是已經附加到JavaVM的線程。通過JNIENV可以獲取到Java的代碼,例如你想在本地代碼中訪問一個對象的字段(field),你可以像下面這樣做:

1.對于類,使用jni_env->FindClass獲得類對象的引用

2.對于字段,使用jni_env->GetFieldId獲得字段ID

3.使用對應的方法(例如jni_env->GetIntField)獲取字段的值

類似地,要調用一個方法,你

step1.得獲得一個類對象的引用obj,

  • step2.是方法methodID。這些ID通常是指向運行時內部數據結構。查找到它們需要些字符串比較,但一旦你實際去執行它們獲得字段或者做方法調用是非常快的。step3.調用jni_env->CallVoidMethodV(obj,methodID,args)。

    從上面的示例代碼,我們可以看出使用原始的JNI方式去與Android(Java)插件交互是多的繁瑣,要自己做太多的事情,并且為了性能需要自己考慮緩存查詢到的方法ID,字段ID等等。幸運的是,Unity3D已經為我們封裝好了這些,并且考慮了性能優化。Unity3D主要提供了一下2個級別的封裝來幫助高效編寫代碼:

    Unity中的原生插件及平臺交互原理是什么

:Unity3D中對應的C/C++橋接器包含在libunity.so中。

  • Level 1:AndroidJNI、AndroidJNIHelper,原始的封裝相當于我們上面自己編寫的C# Wrapper。AndroidJNIHelper和AndroidJNI自動完成了很多任務(指找到類定義,構造方法等),并且使用緩存使調用java速度更快。AndroidJavaObject和AndroidJavaClass基于AndroidJNIHelper和AndroidJNI創建,但在處理自動完成部分也有很多自己的邏輯,這些類也有靜態的版本,用來訪問java類的靜態成員。http://docs.unity3d.com/ScriptReference/AndroidJNI.html,http://docs.unity3d.com/ScriptReference/AndroidJNIHelper.html

  • Level 2:AndroidJavaObject、AndroidJavaClass、AndroidJavaProxy,這個3個類是基于Level1的封裝提供了更高層級的封裝使用起來更簡單,這個在第三部分詳細介紹。

3、iOS插件

iOS編寫插件比Android要簡單很多,因為Objective-C也是C-compatible的,完全兼容標準C語言。這些就可以非常簡單的包一層 extern"c"{},用C語言封裝調用iOS功能,暴露給Unity3D調用。并且可以跟原生C/C++庫一樣編成.a插件。C#與iOS(Objective-C)通信的原理跟C/C++完全一樣:
Unity中的原生插件及平臺交互原理是什么

除此之外,UnityiOS支持插件自動集成方式。所有位于Asset/Plugings/iOS文件夾中后綴名為.m , .mm , .c ,.cpp的文件都將自動并入到已生成的Xcode項目中。然而,最終編進執行文件中。后綴為.h的文件不能被包含在Xcode的項目樹中,但他們將出現在目標文件系統中,從而使.m/.mm/.c/.cpp文件編譯。這樣編寫iOS插件,除了需要對iOSObjective-C有一定了解之外,與C/C++插件沒有差異,反而更簡單。

關于Unity中的原生插件及平臺交互原理是什么就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

仁布县| 响水县| 华亭县| 建宁县| 丹阳市| 寿阳县| 金塔县| 梁平县| 滁州市| 望都县| 昆明市| 萍乡市| 神木县| 拜泉县| 囊谦县| 二连浩特市| 云林县| 台前县| 莎车县| 卢湾区| 新安县| 都匀市| 商都县| 余姚市| 武宣县| 云阳县| 西昌市| 余庆县| 富顺县| 日土县| 南召县| 罗江县| 巫溪县| 桃源县| 金寨县| 二连浩特市| 北票市| 洛扎县| 济南市| 五台县| 盐津县|