前言
使用Unity也有不短的時間了,安卓包也打過不少,但是對Unity與Android的交互卻知之甚少。
因工作需求,需要在Android平台接一些sdk(擴展功能)。我就借此機會了解了下Unity與Android交互的一些知識,並做了一個簡易實現。
現將實現步驟記錄下來以供日后查閱。
一、開發准備
Unity、Android Stuido以及JDK安裝,這些都是基本操作了,網上也有很多教程,這里不細述。
本次開發所使用的軟件版本如下:
Android Studio 3.5
Unity 2018.3.10f1
Java 1.8.0191
二、要實現的功能
要在unity項目中進行安卓功能擴展,有兩種方式:
1、Unity項目導出為Android工程,然后在Android Studio(以后簡稱為AS)中進行二次開發,添加擴展功能。這樣的方式開發起來很靈活,改動起來也很方便,但是就是很麻煩,因為每次改動都要打一回安卓工程。
2、將擴展功能制作成Android庫文件(jar包),然后將jar包導入到Unity中,直接使用。這樣的方式,使用者無法修改這個庫文件的功能,但也更便於使用 。
因為我將要做的功能可能會在團隊內傳播使用,也不需要每個人都去做改動。因此選擇第二種方式。
所以本文的目的就是:
制作一個Android庫文件(jar包),然后在Unity中使用它。
三、如何制作Android庫文件
1、新建Android工程
打開AS,新建一個Android工程,選擇Empty Activity,配置工程名稱、包名、位置以及語言,如下圖
語言最好選Java,因為Java和C#的語法極為相似,學習成本很低。
工程創建之后,默認顯示的是Android視圖下app的工程結構,如下圖。能看到在包名下有一個MainActivity.java的文件,這是安卓app的入口,不過這里並不打算使用它,忽略即可。
2、創建一個Android Library 模塊
選中app,然后右鍵,選Module,在Create New Module窗口中選擇 Android Library
填入Library name、Module name 、Package name以及Language后,點完成。
這里的Package name好像是可以改的,不必和之前創建工程時完全一樣(未驗證)。不過為了少點事,還是先保持一致吧。
現在可以看到,在工程中同時存在着app和mysdk模塊,它倆是平級存在的,並且都有自己的源碼目錄(com.letui.mysdk),以及清單目錄(manifests)。
app部分可以不用管了,后續只對mysdk模塊進行操作。
3、引入Unity對接Android的庫文件
1)在unity的安裝目錄下件,找到一個名為classes.jar的文件
我的目錄為 D:\Unity2018\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono\Development\Classes
2)然后將classes.jar粘貼到mysdk模塊的libs目錄下(需要將工程切換到project視圖)
額外說明:
在il2cpp目錄下也有一個名稱一樣的classes.jar文件,其目錄為
D:\Unity2018\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\il2cpp\Development\Classes
這兩個文件的選擇,與unity的Player Setting中腳本后端設置有關。如下圖
腳本后端用哪個就,就選哪個目錄下的classes.jar
3)選中剛粘貼的classes.jar文件,右鍵,選擇 Add as Library,出現一個彈窗口,默認選中mysdk模塊,直接點確定
然后,這個classes.jar文件就被引入到工程中了,展開三角,可以看到如下三個模塊,其中就有com.unity3d.player。如下
4、創建本模塊的Activity文件
1)展開mysdk模塊下的src目錄,選中com.leitui.mysdk,然后右鍵,新建一個Activity,選擇Empty Activity,輸入Activity Name以及Package Name和Language,勾掉Generate Layout File, 完成。
新建的SDKMainAcivity.java腳本,默認繼承自AppCompatActivity,並帶有一個onCreate方法,如下:
現將SDKMainActivity腳本內容修改為繼續自UnityPlayerActivity ,並添加兩個方法 UnityCallAndroid 和 AndroidCallUnity,
UnityCallAndroid 用來接受Unity的調用,AndroidCallUnity用於向unity發起調用。具體代碼如下:
1 package com.letui.mysdk; 2 3 import androidx.appcompat.app.AppCompatActivity; 4 5 import android.os.Bundle; 6 import android.widget.Toast; 7 8 import com.unity3d.player.UnityPlayer; 9 import com.unity3d.player.UnityPlayerActivity; 10 11 public class SDKMainActivity extends UnityPlayerActivity { 12 13 @Override 14 protected void onCreate(Bundle savedInstanceState) { 15 super.onCreate(savedInstanceState); 16 } 17 18 //unity調用Android 19 public void UnityCallAndroid () { 20 21 Toast.makeText(this,"unity調用android成功", Toast.LENGTH_LONG).show(); 22 23 AndroidCallUnity(); 24 } 25 26 //android調用unity 27 public void AndroidCallUnity () { 28 29 //第1個參數為Unity場景中用於接收android消息的對象名稱 30 //第2個參數為對象上的腳本的一個成員方法名稱(腳本名稱不限制) 31 //第3個參數為unity方法的參數 32 UnityPlayer.UnitySendMessage("receiveObj", "UnityMethod", "This is args."); 33 } 34 }
5、將模塊打包
打包方法有兩種。
一是手動進行構建,然后在Build/intermediates/packaged-classes/release目錄下找到相應的jar包(這個jar包默認名稱為classes.jar,為了區分,需要自己改名稱);
二是用gradle命令。
打開本模塊的build.gradle文件,在文件尾添加如下的一組命令。
//----------------這是一組將module導出為jar的gradle命令------------------- // mysdk為自定義的jar包名稱 //task to delete the old jar task deleteOldJar(type: Delete) { delete 'release/mysdk.jar' } //task to export contents as jar task exportJar(type: Copy) { from('build/intermediates/packaged-classes/release/') into('release/') include('classes.jar') ///Rename the jar rename('classes.jar', 'mysdk.jar') } exportJar.dependsOn(deleteOldJar, build) //---------------------------命令結束------------------------------
這組命令的功能就是打包方法1的自動化版。
build.gradle文件修改后,會提示要求同步,直接同步即可。同步結束,在IDE右上點開Gradle窗口,在other下找到exportJar命令。
雙擊exportJar命令,等一會就會自動生成本模塊的jar文件了。
生成的文件位於mysdk/release/目錄下,如下圖
6、修改AndroidManifest.xml文件
1)打開本模塊的AndroidManifest.xml文件,文件位於mysdk/src/main目錄下,如下圖:
這個文件的內容,目前只有一對application標簽和activity標簽
刪掉這三行,將其內容修改為如下:
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.letui.mysdk"> 4 5 <!-- 這個android:label設置后,unity中ProductName就不生效了,記得改這個--> 6 <application android:label="MySDK"> 7 8 <!-- 這個android:name的值必須為包名+類名--> 9 <activity android:name="com.letui.mysdk.SDKMainActivity"> 10 <intent-filter> 11 <action android:name="android.intent.action.MAIN" /> 12 <category android:name="android.intent.category.LAUNCHER" /> 13 </intent-filter> 14 15 <!-- 這一行不能少,否則會閃退--> 16 <meta-data android:name="unityplayer.UnityActivity" android:value="true" /> 17 </activity> 18 </application> 19 20 </manifest>這其中有三個要注意的點,注釋中都有說明。修改完成后保存。
至此,在AS中的操作就結束了。
四、在unity中使用jar文件
1、新建一個Unity工程。
a.新建一個Unity工程,在Assets目錄下新建Plugins/Android/bin目錄。
b.將第三步修改的AndroidManifest.xml文件拷貝到Assets/Plugins/Android目錄下
c.將第三步生成的mysdk.jar文件拷貝到Assets/Plugins/Android/bin目錄下
完成之后文件結構圖如下:
libs目錄用於存放其它android插件的jar文件,沒有也可以不用創建。
2、制作一個UI界面
a.在SampleScene場景中創建一個Canvas,並創建一個名為"receiveObj"的對象,在receiveObj之下再放一個按鈕和一個Text。
按鈕用於觸發調用Android方法。
Text用於顯示Android調用Unity方法傳遞來的參數。如下圖:
這里要注意,receiveObj的名稱必須與SDKMainActivity類的AndroidCallUnity方法中的UnityPlayer.UnitySendMessage方法的第一個參數保持一致。
b.創建一個SDKTest.cs文件,將腳本掛在receiveObj對象上。如下:
SDKTest腳本的內容如下:
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class SDKTest : MonoBehaviour { private AndroidJavaClass jc; private AndroidJavaObject jo; private Button btn; private Text text; private void Awake() { btn = transform.Find("Button").GetComponent<Button>(); text = transform.Find("Text").GetComponent<Text>(); //這兩行是固定寫法 jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); jo = jc.GetStatic<AndroidJavaObject>("currentActivity"); btn.onClick.AddListener(OnBtnClickHandler); } private void OnBtnClickHandler () { //調用Android中的方法UnityCallAndroid jo.Call("UnityCallAndroid"); } /// <summary> /// 被Android中AndroidCallUnity調用 /// </summary> /// <param name="str"></param> public void UnityMethod(string str) { Debug.Log("UnityMethod被調用,參數:" + str); text.text = str; } }其中必須要有UnityMethod方法,因為它在AndroidCallUnity方法中的UnityPlayer.UnitySendMessage的第二個參數已經指定了。如果不存在的話,調用就會出錯。
jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
這兩句,一個是獲取到UnityPlayer類,一個是獲取到類的實例。
到於為什么這樣能取到我創建的模塊的SDKMainActivity的實例,還有待探究。反正目前這么寫了就行了。3、打包並測試。
在unity中設置Bundle Identifier和Company等信息之后,打一個apk包。
安裝后運行,能正常顯示UI。
點擊按鈕后,顯示了一個Toast,提示“Unity調用Android成功”,說明jar包中的UnityCallAndroid方法被調用。
Unity->Android通信成功。
同時屏幕上方的NewText被變更為“This is args”,"This is args"是 AndroidCallUnity方法中傳遞給UnityMethod方法的參數。
這表示Android->Unity通信成功。
演示見下圖:
調用 前:
,調用后:
五、一點說明
1、模塊的包名和Unity的Bundle Identifier可以不一致(至少在Module模式下,是可以不一致的)。
說明:寫這條是因為其它相關文章全都要求兩邊保持一致,而如果模塊的包名要跟着Unity工程走,也太蛋疼了,所以驗證了下。
2、將AndroidManifest.xml引入到Unity之后,在unity中設置的Product Name就無效了。
需要在AndroidManifest.xml的application標簽中,添加android:label屬性來指定。
初次接觸Android開發,以上內容如有錯誤,還請不吝指出。