最近由于想在Scene的腳本元件中,調用Android的Activity的相關接口,就需要弄明白Scene和Activity的實際對應關系,并對Unity調用Android的部分原理進行了研究。
本文主要探讨Scene和Activity之間的關系,以及Unity打包apk和Android studio打包apk的差别在什麼地方?找到這種差别之後,可以怎麼運用起來?
本文需要用到的工具:
Android反編譯工具——apktool
Android studio自帶的反編譯功能
建立一個Unity項目,建立一個Scene,将Unity工程編譯打包成apk。
對編譯出來的apk,利用apktool進行反編譯:apktool d unityTest.apk
得到的AndroidManifest檔案如下:
由該AndroidManifest檔案可知,系統仍然存在主Activity,名字為com.unity3d.player.UnityPlayerActivity。
言下之意,編譯隻包含Scene的Unity工程,打包成Android apk,會以com.unity3d.player.UnityPlayerActivity作為主程式入口,那麼問題來了,Scene如何加載顯示到這個UnityPlayerActivity呢?
2.1 UnityPlayerActivity
這個就要從UnityPlayerActivity源碼入手了,Android工程中使用UnityPlayerActivity需要依賴到Unity的Android插件classes.jar(位于Unity安裝目錄,可以用everything軟體查找查找得到),對其進行反編譯得到UnityPlayerActivity的部分源碼:
雖然經過混淆,看起來比較費勁,但從代碼this.setContentView(this.mUnityPlayer)可以看出,最終的界面顯示需要依賴到UnityPlayer的執行個體。
另外由于Google也做了一套Unity VR的SDK,與UnityPlayerActivity相對應的類,就是GoogleUnityActivity,下面也對它進行分析。
2.2 從GoogleUnityActivity.java再入手分析
GoogleUnityActivity是google推出的VR SDK中,用于實作Unity Activity的類,通過google查詢其源碼發現:
1. GoogleUnityActivity.java實際上的布局檔案activity_main.xml
布局檔案中沒有具體的内容,隻包含一個FrameLayout布局。
2.重點看GoogleUnityActivity的onCreate函數:
mUnityPlayer作為FrameLayoutView加入到view集合中進行顯示,注意這裡查找的id是android.R.id.content。根據官方對這個id的解釋:
android.R.id.content gives you the root element of a view, without having to know its actual name/type/ID. Check out Get root view from current activity
由此可見,GoogleUnityActivity的實作原理,是建立一個隻包含FrameLayout的空的幀布局,随後通過addView将UnityPlayer中的View加載到GoogleUnityActivity中進行顯示。
看起來跟UnityPlayerActivity有異曲同工之妙,兩者牽涉的類都是UnityPlayer。
2.3.UnityPlayer究竟是一個什麼類呢?
對classes.jar包進行反編譯得到UnityPlayer的部分代碼:
從代碼中可以發現:
UnityPlayer實際上是繼承于FrameLayout;
并且自帶一個currentActivity的成員變量,在構造函數中,直接傳入Activity的相關參數;
在getView函數中直接傳回該FrameLayout;
GoogleUnityActivity通過UnityPlayer的構造函數,将其context傳遞給UnityPlayer,并指派給其成員變量currentActivity。
由于UnityPlayer類做了混淆,關于渲染的核心功能也封裝在native代碼中,關于Scene轉換到到UnityPlayer作為FrameLayout,隻能做一個簡單的推測:通過調用Android的GL渲染引擎,在native層進行渲染,并同步到FrameLayout在UnityPlayerActivity上進行顯示。
從以上研究的内容可知,假如要從要實作将Scene顯示在固定的Activity當中,則需要對Activity的oncreate部分的countview和unityplayer進行處理。最簡單的方法是寫一個直接繼承于UnityPlayerActivity或GoogleUnityActivity的類,并在類中寫所需要的Unity調用Android的方法。
這樣Scene就會加載在特定的Activity當中,Unity c#通過擷取currentActivity變量就可以擷取到該Activity,并調用其中的函數。
Android studio工程包含多個module的依賴,則需要将對應的module編譯的插件一起拷貝Plugins/Android/lib目錄當中。
在第一步驟下,可以直接删除打包後的aar library目錄,尤其裡面假如帶有unity的Android插件classesjar,否則會編譯報錯。
多個module編譯的時候,注意manifest lablel相關設定,另外就是build.gradle的minSDKVersion資訊。否則會出現manifest merger失敗的錯誤。
關于Unity的Android Manifest檔案合并:
Unity編寫一個Scene,Android studio寫一個包含主Activity的aar包,放在Plugins/Android目錄當中。用Unity編譯apk出來之後,反編譯他的AndroidManifest檔案,兩個主Activity,預設顯示包含Scene的Activity。
解決方法:Unity的Manifest檔案合并,把一個manifest放到Plugins/Android目錄下,就不會合并manifest了。
由于Unity開發Android時,常常設計到Unity + Visual和Android studio的環境切換,Unity的開發往往會更快一些,更多的是Android java側的代碼編寫和調試。
這種情況時,有沒有一種方法,能夠将Unity編譯好的Unity Scene和c#相關檔案,放到Android studio中進行打包,進而實作直接在Android studio中進行調試?
方法原理倒是很簡單,通過對比Unity打包的apk,與普通的Android apk的檔案差别,找出Unity檔案存放的目錄,随後對應存放到Android studio工程目錄中,最後通過Android studio完成對Unity相關檔案的打包。
首先将apk添加zip的字尾,友善用beyond compare進行對比:
發現隻是多了assert/bin目錄,在這個目錄之下,可以看到unity相關dll庫
将該檔案,拷貝到Android studio工程的src/main/assert目錄之下;
在Android studio調試時,可以将aar library工程設定為app工程,這樣就可以編譯apk運作到手機了。
用Android studio對該工程進行編譯,發現assert/bin目錄成功被打包進去。
直接apk install 運作,可以看到跟Unity編譯打包的apk,是相同的效果。
相反,假如Android工程調試好之後,則直接編譯成app模式修改成library模式,進行build之後,就會生成aar庫,此時将aar庫拷貝到Plugins/Android/lib目錄當中,注意要删除aar庫中的assert/bin,因為這個目錄是我們先前從Unity拷貝過去的,假如不删除,在unity裡面會出現重複打包導緻的檔案沖突的情況。
由于當将Unity打包之後的bin目錄拷貝到Android studio工程之後,Android studio此時是一個library工程,需要轉換為app工程。
關于這其中涉及到的Android studio library和app的轉換,通過設定build.gradle檔案來實作:
app模式:apply plugin: 'com.android.application'
library模式:apply plugin: 'com.android.library'
不過在設定這兩種模式時,需要注意applicationId "com.example.yin.myapplication"的設定,假如是library模式,則需要直接注釋掉。
假如Android的java部分重新調試好之後,重新将app模式改成library模式,進行build,将生成的aar包,拷貝到Unity Android Plugin目錄中,就可以直接在Unity看運作效果了。
不過一定要記得删除Android studio打包的aar檔案裡面的assert/bin目錄,以防止在Unity中重複打包。
Unity中的Scene在Android中,其實對應于Activity的FrameLayout,每個Scene的運作都有其Activity環境,通過currentActivity變量可以擷取得到。
要實作自定義的Activity能夠具備直接加載Scene的功能,則需要其繼承于UnityPlayerActivity或者GoogleUnityActivity,再或者,直接自定義實作UnityActivity類。
提升Unity+Android Plugin項目開發效率的方法:
● 直接将Unity打包的apk中的assert/bin目錄拷貝到Android studio工程的src/main/assert目錄當中,并且将Android工程配置成app模式,就可以直接在Android studio上面,對整個Unity+android plugin的工程進行調試。
● Android studio部分調試好之後,需要修改build.gradle檔案,重新将app模式修改為library模式,編譯出aar封包件,删除原來拷貝過來的unity部分,放入到unity的Plugins/Android/lib目錄下進行使用即可。
最後套句名言:log打得好,bug解得早
相關推薦