【前言】
unity接入Android SDK有兩種方式,一種是把Unity的工程導(dǎo)出google project的形式進(jìn)行接入,另一種是通過把Android的工程做成Plugins的形式進(jìn)行接入。我們接入SDK基本都是將SDK作為插件的形式接入的。
對我們接入SDK的人來說,SDK也是分等級的:
第一等級:只有so文件,這種so文件里一般是算法居多,他們不懂得如何做成SDK,這時(shí)需要做這個JAR中間層,C#調(diào)用JAR接口,JAR接口調(diào)用so文件接口,如果你懶得做,可以讓給so文件的人做,本來就是他們應(yīng)該做的?;蛘咧苯佑肅#調(diào)用C++的方式來接入。如果連so文件都沒給,可以直接開噴,一般不會出現(xiàn)這種情況。
第二等級:有Androrid SDK和接入文檔,我們知道Android SDK一般包含so文件和jar,理論上我們可以直接在C#端調(diào)用了。如果是功能簡單的SDK可以這樣做,如果SDK比較復(fù)雜,尤其是涉及Activity有生命周期的,必須自己再寫個Jar作為中間層。這時(shí)候,如果接入文檔不清晰就有很多坑。如果有直接的SDK對接人,任何不清楚的地方就要直接問。很多SDK不是一般不是面向Unity的,都在這個范圍內(nèi)
第三等級:有Android SDK和中間層Jar、接入文檔。這種SDK接入起來比較舒服Jar層大的框架都給你搭建好了,你肯定要拿到Jar的代碼的,根據(jù)項(xiàng)目需要和使用SDK的需要,在Jar層修改些代碼,在C#層調(diào)用就可以了。這種一般是面向Unity的SDK。
第四等級:有Android SDK和中間層Jar以及C#層調(diào)用接口。這種SDK接起來最輕松,只需要根據(jù)需要在C#調(diào)用接口,寫回調(diào)函數(shù)即可,看文檔的時(shí)候注意調(diào)用什么方法,參數(shù)都是什么,代表什么意義,回調(diào)參數(shù)都是什么即可。一般這種SDK是比較成熟的可以面向Unity的大型SDK,接口回調(diào)什么的都會給你設(shè)置好。
SDK接入多了熟練了之后,你會發(fā)現(xiàn)接入SDK是一件無聊又費(fèi)時(shí)的事情,你不知道別人家的SDK會不會在文檔中沒說明什么問題,坑很多,Android的和iOS的有時(shí)候又往往不一樣。SDK的測試相比GamePlay等在Editor上就能測試的而言會繁瑣很多,你必須需要等到出包之后才能進(jìn)行驗(yàn)證和測試,Android和iOS都要測一遍,甚至不同的機(jī)型也要測下,看Log還需要連接adb拿log,自己看不懂log還要交給對方去看,需要來回溝通。為了提高效率,必須要了解如何熱更和打包。
【接入SDK的兩種方式】
第一種是,將 Unity 在安卓平臺選擇 Gradle 打包出來,然后在Android Studio中接入原生SDK,并生成APK
第二種是,將SDK做成 Unity Plugin,生成jar或arr在C#層調(diào)用。
基本上我們都是用第二種方式接入的。
【UnityPlayer與UnityPlayerActivity】
Activity生命周期
游戲啟動時(shí)調(diào)用MainActivity,也即UnityPlayerActivity。(以下代碼源自反編譯)
按照Activity的生命周期,先調(diào)用onCreate(),onCreate時(shí)new了一個UnityPlayer
UnityPlayer實(shí)例化時(shí)主要做了以下事情:
- 指定currentActivty,上下文mContext;
- preloadJavaPlugins()預(yù)加載JavaPlugins,主要是調(diào)用com.unity3d.JavaPluginPreloader,其是Unity引擎的一個Java類,用于在Android應(yīng)用程序啟動時(shí)提前加載相關(guān)插件,以提高應(yīng)用程序的性能和穩(wěn)定性
- loadNative(getUnityNativeLibraryPath(var1))中加載libmain.so文件,libmain.so是一個動態(tài)共享庫文件,用于處理Unity游戲引擎的啟動和初始化過程,該庫包含了Unity引擎的主要邏輯和函數(shù),包括資源管理、場景管理、物理引擎、渲染引擎等。
- this.mContext.registerReceiver(this.mKillingIsMyBusiness,newIntentFilter("com.unity3d.intent.action.SHUTDOWN"))?使用Activity的registerReceiver()方法來注冊一個BroadcastReceiver,并指定想要監(jiān)聽的廣播時(shí)間類型為com.unity3d.intent.action.SHUTDOWN,收到該事件時(shí)指定onReceive方法,也即finish方法,這finish方法是Android系統(tǒng)的finish(),用于結(jié)束當(dāng)前Activity并退出應(yīng)用程序
- this.m_MainThread.start() unity主線程開始運(yùn)行(前面進(jìn)行過實(shí)例化),設(shè)置了線程名字為UnityMain。
?
當(dāng)暫停時(shí),調(diào)用UnityPlayer的Pause方法, 如果這個暫停是結(jié)束,會調(diào)用到UnityPlayer.ShutDown,否則調(diào)用Semaphore.release()用于釋放一個許可證并增加信號量的計(jì)數(shù)器,以便其他線程可以獲取許可證并訪問共享資源。調(diào)用Semaphore.drainPermits來判斷是否需要Destroy。
(Semaphore 是 Java 中的一個多線程同步工具,它可以用來控制并發(fā)線程數(shù),防止出現(xiàn)線程安全問題。在 Semaphore 中,許可證的數(shù)量表示可以同時(shí)執(zhí)行的線程數(shù)。當(dāng)一個線程需要執(zhí)行某個操作訪問共享資源時(shí),它需要先調(diào)用acquire()方法獲取一個許可證,這會減少信號量的計(jì)數(shù)器,如果當(dāng)前許可證的數(shù)量為 0,那么這個線程就會被阻塞。當(dāng)共享資源被釋放時(shí),線程需要調(diào)用release()方法來釋放許可證并增加信號量的計(jì)數(shù)器。而調(diào)用 Semaphore.drainPermits() 方法后,Semaphore 中所有的許可證都會被強(qiáng)制釋放。這個方法通常在一些特殊情況下使用,比如在程序退出時(shí),需要強(qiáng)制釋放 Semaphore 中的所有許可證,以確保程序能夠正確退出。)
?
?當(dāng)Resume時(shí),調(diào)用UnityPlayer的Resume方法,最終會調(diào)用UnityPlayer.this.nativeResume(),其會繼續(xù)調(diào)用Unity游戲引擎底層的C++代碼,以便在Android系統(tǒng)中恢復(fù)Unity的渲染和邏輯處理。同時(shí)主線程Resume。
?當(dāng)Destory時(shí),調(diào)用UnityPlayer的quit方法,在該方法中主線程銷毀,卸載libmain.so
這些會經(jīng)過Unity C++ NativeCode會調(diào)用到C#的 OnApplicationPause、OnApplicationFocus、OnApplicationQuit
?UnitySendMessage
UnitySendMessage最終調(diào)用了nativeUnitySendMessage
?還有其他的一些例如各種輸入事件等,都是從Java調(diào)用Unity的C++ NativeCode,繼而調(diào)用我們平常使用的Unity C# API
?可以看到UnityPlayerActivity是Activity和UnityPlayer的中間層,核心都在UnityPlayer中。
【在Unity中接入SDK】
如果把前面的文章都看完了,那么對于如何接入SDK就有了一定的了解,理順一些初始化、調(diào)用關(guān)系等在實(shí)現(xiàn)邏輯上應(yīng)該沒有疑惑了。
接入單個SDK
先判斷這個SDK需不需要生命周期,如果需要可以繼承UnityPlayerActivity,在onCreate等方法中調(diào)用SDK的方法等,其他的寫一些Java方法,讓C#可以調(diào)用即可。
如果有生命周期,又不想繼承UnityPlayerActivity,可以在Java通過onCreate等調(diào)用SDK的方法改為C#通過OnApplicationPause等調(diào)用。
如果SDK不需要生命周期,在Java里寫入一些初始化方法,參數(shù)從C#調(diào)用時(shí)傳遞進(jìn)去即可,如果SDK僅需要Activity參數(shù),獲取到UnityPlayer的curActivty傳遞進(jìn)去即可。
如果SDK必須有獨(dú)立的Activity,那就在Java中通過Intent調(diào)用另一個Activtiy,也就是如何切換Activty的問題。
接入多個SDK
考慮接入單個SDK時(shí),我們有三層,C#、JAR、SDK,在C#層我們會將業(yè)務(wù)層直接調(diào)用C#方法,在Jar層我們會直接調(diào)用SDK的方法。
如果要接入多個SDK每個SDK分開接入的話,就會有多個上述的三層,不好管理。我們自然而言的就會想到合并,合并就是要做一個中間層,供業(yè)務(wù)層和SDK通信。這個中間層就是中間件。
中間件包括C#層和Jar層,原來業(yè)務(wù)層是直接調(diào)用C#方法,C#調(diào)用Java方法,Java方法里調(diào)用SDK方法,現(xiàn)在是業(yè)務(wù)層調(diào)用中間件的C#層,C#調(diào)用Jar層,Jar層調(diào)用SDK的方法。
我們知道SDK提供的方法可以分為以下幾類:
初始化相關(guān)的方法A、可能的生命周期方法B、業(yè)務(wù)需要的功能方法C
假設(shè)要接入三個SDK: X Y Z
那么我們至少要調(diào)用9個方法(3x3)
我們在調(diào)用方法時(shí)有三個選擇:一個是直接在C#調(diào)用某個SDK的某個方法,例如AndroidJavaClass.Call(XA)。另一個是在C#傳入?yún)?shù),間接調(diào)用,在Java端再直接調(diào)用方法,例如調(diào)用SDK的初始化方法時(shí),將SDK類型作為參數(shù),在Java端根據(jù)類型再調(diào)用對應(yīng)SDK的初始化方法。三是更進(jìn)一步的是將調(diào)用的方法名字也作為參數(shù)傳入。
至于使用哪種方式看你自己,推薦的方式是中間件的方法直接調(diào)用,SDK的初始化和聲明周期方法間接調(diào)用,功能方法全參數(shù)調(diào)用。
回調(diào)參數(shù)設(shè)計(jì)
我們有多個SDK的多個方法,但回調(diào)時(shí)Unity只給有一個string類型的參數(shù),為了解決各種不同類型的回調(diào),我們自然而然想到的是用json字符串。格式如下:?
{
"sdkname": "xxx",
"methodname": "xxx",
"result": 0,
"data": {
"type": "xxx.",
"filename": "xxx",
"country": "xx"
}
在回調(diào)用方法中,將Json字符串轉(zhuǎn)為C#對象,依次switch sdkname、methodname,創(chuàng)建對應(yīng)的處理方法,有一個簡單的result用于區(qū)分同一個方法在不同情況下的回調(diào),回調(diào)的結(jié)果放在data里,每個方法有各自不同的data。
之后如果有新增的Sdk,直接創(chuàng)建對應(yīng)的方法即可。
中間件結(jié)構(gòu)設(shè)計(jì)
C#層:
C# SDKManager類,其中包含業(yè)務(wù)調(diào)用的接口,持有各種不同的SDK基類(將不同的SDK當(dāng)作組件),及回調(diào)函數(shù)
各種SDK基類及其PC子類、Android子類、iOS子類,在Android子類中調(diào)用Jar層方法,相同功能的SDK可以再抽象出一個基類
Jar層:
JavaSDKManager類,其中包括C#層調(diào)用的接口,持有各種SDK類,及UnitySendManager等
各種SDK類包含調(diào)用SDK的方法。文章來源:http://www.zghlxwxcb.cn/news/detail-560686.html
【參考】
Unity3d Android SDK接入解析(二)Unity3d Android SDK的設(shè)計(jì)與兩種接入方式_android unity 3d app 架構(gòu)_小楊在玩iOS的博客-CSDN博客文章來源地址http://www.zghlxwxcb.cn/news/detail-560686.html
到了這里,關(guān)于Unity與Android交互(4)——接入SDK的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!