前言:
????Unity和android交互,這個(gè)問(wèn)題,在百度搜一下,解決方案還是蠻多的,起初我也是照著百度出來(lái)的帖子一步步實(shí)現(xiàn)了.但是!大部分帖子只管功能實(shí)現(xiàn)根本不講緣由,且實(shí)現(xiàn)的功能比較死板.看完這樣的帖子,對(duì)學(xué)習(xí)完全沒(méi)有幫助啊喂!想要在項(xiàng)目中使用交互代碼相應(yīng)的也會(huì)遇到許多問(wèn)題(后面會(huì)說(shuō)遇到的問(wèn)題,以及解決方案).
適用人群:
1.unity開(kāi)發(fā)者,不會(huì)使用AndroidStudio,通過(guò)本教程,可以了解Android Studio的基本使用.
2.搜索了很多unity,Android交互文章卻沒(méi)找到合適解決方案的同學(xué).
使用jar還是aar進(jìn)行通信?
Jar中包含了原生java代碼,如果我們只是測(cè)試通信,一個(gè)簡(jiǎn)單的jar包就能夠通信,然而實(shí)際開(kāi)發(fā)中,一般不可能只用一個(gè)jar包,在android工程中一般都會(huì)引用很多jar包,
aar大概是設(shè)計(jì)出來(lái)用來(lái)替代jar來(lái)進(jìn)行通信的,不僅包含了jar 還包含了提供的android資源(圖片,布局文件等).這也是aar包比jar大的原因,我們可以通過(guò)刪除不相關(guān)內(nèi)容來(lái)降低aar包的大小.
AndroidStudio:
打開(kāi)AndroidStudio-? ? NewProject:
新建activity之后,要在activity中引用unity的方法就需要引入unity的jar包,根據(jù)以下目錄找到unity提供的jar:
根據(jù)在unity中選擇的打包方式,復(fù)制出來(lái)文件夾中的jar包
放在android studio中的libs文件夾中
這個(gè)classes.Jar是unity給安卓寫(xiě)的腳本,在android中引用了這個(gè)jar包,就可以調(diào)用里面的方法(各位原生老哥應(yīng)該比我清楚....),右鍵classes.jar 最下面有個(gè)add as a Library 或者
導(dǎo)入成功后,就可以引入unity提供的package了
根據(jù)我想實(shí)現(xiàn)的需求,需要引用兩個(gè)package:
1. 在安卓原生中調(diào)用unity方法(向unity傳遞數(shù)據(jù)),import com.unity3d.player.UnityPlayer
2. 為了unity中能找到并調(diào)用andorid方法( import com.unity3d.player.UnityplayerAcitvity,來(lái)繼承UnityPlayerActivity)
對(duì)默認(rèn)的activity進(jìn)行以下更改:
Android Studio端 邏輯代碼編寫(xiě)完畢.這里講解下unity android相互調(diào)用API的使用規(guī)則:
Android調(diào)unity:
?unitySendMessage需要三個(gè)參數(shù):s1(string):unity 場(chǎng)景中的的gameobject的name .s2(string):該gameobject上的腳本上的方法名 s3(string):傳遞的消息內(nèi)容,可以是json,xml之類(lèi)的.也就是說(shuō)unity通過(guò)尋找場(chǎng)景中的gameobject,調(diào)用unity自己的sendmessage來(lái)傳遞消息(這里使用的是Unity提供的unitysendMessage往unity回調(diào)數(shù)據(jù),它是通過(guò)反射實(shí)現(xiàn)的,這個(gè)帖子http://www.reibang.com/p/f5b20d43315a提供了java中反射的實(shí)現(xiàn),可以不用unity的方法直接實(shí)現(xiàn)通信).
Unity調(diào)用android:
Unity官網(wǎng)提供的死代碼以及網(wǎng)上一半帖子的代碼如上,大概意思是通過(guò)包名找到了java類(lèi), 但是是具體哪一個(gè)類(lèi)的對(duì)象需要用”currentActivity”獲取到.這里我推測(cè)我們?cè)赼ndroid 中新建的MainActivity是繼承了UnityPlayerActivity才能被以上兩行代碼找到(這個(gè)必須要繼承UnityPlayerActivity 才能被找到 很雞肋很雞肋).
接下來(lái)導(dǎo)出aar包:
Android Studio默認(rèn)build會(huì)生成apk,此時(shí)需要更改build.gradle來(lái)讓Android studio導(dǎo)出aar包
設(shè)置完畢后,點(diǎn)擊build-RebuildProject進(jìn)度走完后,打開(kāi)build/outputs/aar找到導(dǎo)出的aar
右鍵,show In Explorer在文件夾中顯示它
可以看到它是可以被解壓軟件打開(kāi)的,我們可以打開(kāi)看下,來(lái)刪除那些我們不需要的內(nèi)容
由于在Android Studio我無(wú)腦build導(dǎo)致build出來(lái)一大堆用不到的東西,我又不知道如何在Android studio中不導(dǎo)出這些,所以現(xiàn)在直接操作aar刪除,首先要分辨出來(lái)哪些是有用的,哪些是無(wú)用的.
有用的:我寫(xiě)的兩個(gè)java類(lèi),猜測(cè)被自動(dòng)打入到aar包中外層的Classes.jar中了
無(wú)用的:事先從unity導(dǎo)入的jar包(存放在libs/classes.jar,至于它為啥和aar中的classes.jar名字一樣,我也不知道),布局文件,dpi文件 刪除完畢后,如下
Unity中:
在unity的 Assets目錄下新建Plugins/Android(Plugins文件夾必須是在Assets根目錄) 文件夾 拖入剛才的aar和AndroidManifest(此處應(yīng)該有疑問(wèn),為什么aar包中有AndroidManifest文件,還要在unity的目錄下還放置一個(gè)AndroidManifest)
在unity中新建name為AndroidCall的gameobject以及新建腳本,新建方法TestCall.
unity playersettings中packageName和Android activity的package要保持一致
大部分帖子寫(xiě)到這里展示一下調(diào)用成功的截圖,就結(jié)束了帖子...前面講到的很雞肋的unity獲取Android 對(duì)象的方式,就這樣雞肋下去了,準(zhǔn)確的來(lái)說(shuō),目前實(shí)現(xiàn)的unity和Android交互只是一個(gè)最基礎(chǔ)的demo,在實(shí)際項(xiàng)目開(kāi)發(fā)過(guò)程中根本不可能使用這種單一的方式.我相信這些朋友后面也都發(fā)現(xiàn)了它的缺點(diǎn),一部分網(wǎng)友更新了unity Android交互第二篇...,我認(rèn)為要想得心應(yīng)手的使用unity和底層通信就要了解其通信原理,但是在介紹通信原理之前必須要介紹一下AndroidManifest這個(gè)東西,它對(duì)我們理解如何通信至關(guān)重要.
AndroidManifest是什么?他有什么作用?
清單文件(Mainfest)普遍存在于各個(gè)平臺(tái)生成的項(xiàng)目中,是對(duì)指定內(nèi)容的一種描述,提供了指定內(nèi)容構(gòu)成有關(guān)的所有信息,且有唯一標(biāo)識(shí)作用,就像是一條http請(qǐng)求,header包含了對(duì)body體內(nèi)容的描述.
一般由以下幾部分構(gòu)成:
1.指定內(nèi)容的版本信息.
2.指定內(nèi)容對(duì)其他內(nèi)容的依賴(lài)
3.指定內(nèi)容的安全權(quán)限
AndroidManifest是Android中打包時(shí)生成的一個(gè)清單文件,位于根目錄:
1.包含了app要在Android系統(tǒng)上運(yùn)行的基礎(chǔ)信息:app圖標(biāo)(Icon),名稱(chēng)(product Name),橫屏還是豎屏(orientation),版本號(hào)(version)等等,在unity中是通過(guò)PlayerSettings的可視化界面配置app的基本信息的.這些內(nèi)容會(huì)在打包的時(shí)候?qū)懭階ndroidManifest中,有了這些基礎(chǔ)設(shè)置,該app安裝在Android設(shè)備上才會(huì)在桌面中顯示出來(lái).
2.設(shè)置程序入口.有了上一條說(shuō)的基礎(chǔ)配置之后,雖然能夠在Android中顯示,但是沒(méi)有設(shè)置要啟動(dòng)的Activity,會(huì)直接閃退.(平時(shí)正常打包出來(lái)的app能運(yùn)行是因?yàn)橐呀?jīng)設(shè)置好了程序入口Activity),在Android中應(yīng)該是允許配置多個(gè)activity,設(shè)置一個(gè)mainActivity,然后在activity中切換.unity打的Android只使用一個(gè)activity.
3.配置該app需要的Android功能:比如app需要開(kāi)啟攝像機(jī)或者麥克風(fēng),需要在Manifest中配置權(quán)限,否則Android不會(huì)彈出權(quán)限請(qǐng)求,權(quán)限部分需要<uses-permission>標(biāo)簽,再比如需要Android廣播接收器功能就需要配置<receiver>標(biāo)簽.
????Android開(kāi)發(fā)的同學(xué)都知道,每當(dāng)新建一個(gè)Activity ,就會(huì)生成一個(gè)Manifest文件,它用來(lái)指定當(dāng)前activity需要的各種配置.然后一個(gè)activity就可以通過(guò)設(shè)置gradle 來(lái)生成一個(gè)apk,運(yùn)行在Android設(shè)備上,那unity是如何生成apk的呢?我們新建一個(gè)unity工程并導(dǎo)出成Android Project來(lái)研究一下:
新建一個(gè)空的unity工程,將unity 的buildSettings上的export project勾選就可以將項(xiàng)目導(dǎo)出成Android project
使用AndroidStudio打開(kāi)該工程發(fā)現(xiàn),unity的export過(guò)程就是將unity的內(nèi)容導(dǎo)出成了一個(gè)activity,叫做UnityPlayerActivity,這個(gè)activity的packageName就是在unity的Android PlayerSettings中設(shè)置的packageName
前面說(shuō)到每個(gè)activity都包含Manifest文件,打開(kāi)Manifest文件如下
Unity打包出來(lái)的Manifest文件是根據(jù)存放在unity安裝目錄下的Manifest模板生成一個(gè)com.XXX(unity中設(shè)置的package).UnityPlayerActivity,所以我們unity的apk在Android設(shè)備中是通過(guò)Manifest中設(shè)置的啟動(dòng)項(xiàng)Activity啟動(dòng)的.
????????下面回過(guò)頭來(lái)看剛才那個(gè)測(cè)試demo的Manifest文件:
主要功能就是設(shè)置了com.axin.mylibrary.customActivity這個(gè)activity為啟動(dòng)activity,已知unity會(huì)自動(dòng)生成UnityPlayerActivity這個(gè)activity作為啟動(dòng)項(xiàng),如何把咱們的activity替換到Unity生成的Manifest文件中呢?官方文檔對(duì)此進(jìn)行了說(shuō)明:https://docs.unity3d.com/2017.4/Documentation/Manual/android-manifest.html?tdsourcetag=s_pctim_aiomsg
在unity中AndroidManifest文件的合并重點(diǎn)是這一句(已使用谷歌翻譯)
在unity中提供的AndroidManifest文件只能有一個(gè),它的目錄是Assets/Plugins/Android/AndroidManifest.xml,這也是我們前面在unity目錄下導(dǎo)入AndroidManifest的原因,下面展示下unity替我們合并的Manifest文件
? ??對(duì)比之前打包的空工程的AndroidManifest文件,啟動(dòng)項(xiàng)activity已經(jīng)變成了.customActivity,這就是我們?cè)赼ssets/plugins/Android目錄下放置的AndroidManifest文件中的內(nèi)容覆蓋了unity的fest文件模板生成的效果.
????由于unity的playersettings中設(shè)置的packageName和 aar包中Android activity的packge一致(此處應(yīng)該有疑問(wèn),我unity中的packageName應(yīng)該指定的是我司的公司名稱(chēng)和項(xiàng)目名稱(chēng),怎么能和第三方aar保持一致呢?也不能我用了阿里巴巴的aar,我就要讓我unity項(xiàng)目的packageName帶上阿里巴巴吧?),所以啟動(dòng)項(xiàng)activity就是com.axin.mylibrary.customactivty,另外customactivty又是繼承UnityPlayerActivity的
所以我們的代碼
獲取的就是這個(gè)java類(lèi)的對(duì)象(為了能用上述兩行代碼找到我們定義的activity,實(shí)際上是我們的activity搶占了unity默認(rèn)的啟動(dòng)activity,這會(huì)帶來(lái)不同程度的后果,但是如果我們沒(méi)有使用UnityPlayerActivity這個(gè)父類(lèi)的需求的話,我們盡量不要繼承它實(shí)現(xiàn)通信,下文會(huì)提到另一種調(diào)用javaclass對(duì)象的方法.)
????????此時(shí)調(diào)用AndroidCallUnity方法就會(huì)調(diào)用unity中的方法了.
? ??????以上簡(jiǎn)單的解釋了下我們的通信demo是如何運(yùn)行的,但是它僅僅是demo,下面我們分析一下實(shí)際項(xiàng)目的需求:
A.在unity中接入一個(gè)或者多個(gè)第三方sdk,意味著導(dǎo)入多個(gè)aar.
B.在Android原生中接入多個(gè)原生sdk,然后將數(shù)據(jù)傳回unity.
C.Unity項(xiàng)目最終只作為一個(gè)activity來(lái)運(yùn)行,如果各個(gè)aar都要搶占Activity入口,都要求unity項(xiàng)目packageName和自己保持一致那該怎么辦?(上文的測(cè)試demo搶占了Activity入口且要求項(xiàng)目packageName和aar的保持一致.網(wǎng)上搜索的unity Android通信帖子大部分都這樣寫(xiě)的)
????????情況A多為unity開(kāi)發(fā)者遇到,意味著unity中需要導(dǎo)入多個(gè)aar,就可能(我覺(jué)得導(dǎo)出的aar都會(huì)有Manifest文件,因?yàn)槲乙膊皇茿ndroid開(kāi)發(fā),不敢說(shuō)太絕對(duì))會(huì)附帶多個(gè)Manifest.多個(gè)AndroidManifest可能存在于unity項(xiàng)目工程中,也可能存在于aar包中.
對(duì)于Unity工程里的AndroidManifest:
眾多AndroidManifest可能擁有不同的系統(tǒng)權(quán)限或不同的APILevel等.這時(shí)候就應(yīng)該合并不同的Manifest.前面說(shuō)到unity的項(xiàng)目中只能存在一個(gè)AndroidManifest(起初我以為AndroidManifest只要放在Android目錄下就可以,unity中的editor文件夾,lib文件夾貌似都是這個(gè)邏輯,覺(jué)得多個(gè)Manifest文件unity會(huì)幫我們合并,事實(shí)上只要assets/Plugins/Android下的fest文件才能被合并,害我在這里卡住了很久),所以就需要手動(dòng)合并這些Manifest,只保留一份在Plugins/Android目錄下.保證手動(dòng)合并的Manifest中只有一個(gè)MainActivity.且這個(gè)activity盡量不主動(dòng)設(shè)置,讓unity去生成默認(rèn)的就可以.合并方法是復(fù)制粘貼....,即把非指定路徑下的Manifest文件中和指定路徑下Manifest文件不同的東西復(fù)制過(guò)去...比如權(quán)限什么的,最終文本還是要符合x(chóng)ml結(jié)構(gòu)的.
對(duì)于存在于aar包中的Manifest文件:?
????????打包過(guò)程中會(huì)根據(jù)谷歌的固定算法進(jìn)行合并,unity打包apk的流程大概是先打成Android 工程,再利用Android中的Gradle的Task打出apk,谷歌的合并算法應(yīng)該是在Task里,和unity沒(méi)什么關(guān)系,情況B和這個(gè)情況類(lèi)似,AndroidStudio也是依賴(lài)Gradle打包的.關(guān)于AndroidStudio中Manifest的合并原理:http://www.reibang.com/p/0febcb3625d9
AndroidManifest總結(jié):
????????根據(jù)分析unity中export的Android project分析發(fā)現(xiàn),unity是根據(jù)Unity的存放目錄下的AndroidManifest模板和Assets/Plugins/Android/AndroidManifest.XML 合并生成一個(gè)AndroidManifest,作為unityPlayerActivity的配置文件.unity項(xiàng)目目錄下的fset文件和aar包中的fest文件不同之處在于,unity中的fest通常搶占了入口Activity或?qū)nity默認(rèn)Activity進(jìn)行了一些修改,而aar包中的fest文件通常只包含該aar需要的系統(tǒng)權(quán)限和API Level ,該部分會(huì)在Gradle的task中被谷歌算法合并.
情況C的解決方案:
1.如果接入的第三方sdk都是原生Android Sdk,則可以在Android Studio中新建一個(gè)和Unity工程packageName一樣的Activity,整合眾多sdk要使用的API在一個(gè)Activity里,然后這個(gè)Activity設(shè)置為Main入口,放置新的AndroidManifest文件在unity指定文件夾下,下面這個(gè)教程貼是這么做的:https://blog.csdn.net/LIQIANGEASTSUN/article/details/78805902
2.不使用activity,直接使用Java類(lèi),Unity中可以通過(guò)new AndroidJavaObject(“包名”)直接生成一個(gè)java對(duì)象,來(lái)調(diào)用其中的方法(直接繼承UnityPlayerActivity主要是方便查找到activity ),各個(gè)sdk自己各自維護(hù)一個(gè)java類(lèi),提供unity調(diào)用的方法,在unity中通過(guò)unity的方式直接new這個(gè)java類(lèi)來(lái)調(diào)用(需要提供包名+類(lèi)名),在unity中整合各個(gè)第三方sdk,此方案不需要在AndroidStudio中新建activity,因?yàn)椴恍枰獡屨糰ctivity所以也不需要在unity文件夾下放置AndroidManifest,打包過(guò)程中會(huì)自動(dòng)合并aar包中的fest.
步驟如下:
首先,在AndroidStudio中新建一個(gè)java類(lèi):customSendMessage
導(dǎo)入unity中包含sendmessage的package(如何導(dǎo)入在上文) ,寫(xiě)一個(gè)AndroidCallUnity 方法
將aar導(dǎo)出后直接放到plugins/Android目錄下,(導(dǎo)出步驟在上文),在unity中
在java中有可能要用到上下文,這里的上下文(什么是上下文可以百度搜一下,大概指的就是當(dāng)前運(yùn)行的程序域吧)指的是當(dāng)前的Activity,獲取當(dāng)前activity 返回給java
文章完畢,如有錯(cuò)誤,還請(qǐng)各位大佬糾正,我會(huì)第一時(shí)間進(jìn)行修改,謝謝大家.
我的CSDN地址:
https://blog.csdn.net/weixin_39106746/article/details/103469620