天天看點

【Unity與Android】01-Unity與Android互動通信的簡易實作

前言

使用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,配置工程名稱、包名、位置以及語言,如下圖
【Unity與Android】01-Unity與Android互動通信的簡易實作

 語言最好選Java,因為Java和C#的文法極為相似,學習成本很低。

工程建立之後,預設顯示的是Android視圖下app的工程結構,如下圖。能看到在包名下有一個MainActivity.java的檔案,這是安卓app的入口,不過這裡并不打算使用它,忽略即可。

【Unity與Android】01-Unity與Android互動通信的簡易實作

2、建立一個Android Library 子產品

 選中app,然後右鍵,選Module,在Create New Module視窗中選擇 Android Library 
【Unity與Android】01-Unity與Android互動通信的簡易實作

 填入Library name、Module name 、Package name以及Language後,點完成。

 這裡的Package name好像是可以改的,不必和之前建立工程時完全一樣(未驗證)。不過為了少點事,還是先保持一緻吧。

【Unity與Android】01-Unity與Android互動通信的簡易實作
 現在可以看到,在工程中同時存在着app和mysdk子產品,它倆是平級存在的,并且都有自己的源碼目錄(com.letui.mysdk),以及清單目錄(manifests)。
【Unity與Android】01-Unity與Android互動通信的簡易實作
 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中腳本後端設定有關。如下圖

  

【Unity與Android】01-Unity與Android互動通信的簡易實作

    腳本後端用哪個就,就選哪個目錄下的classes.jar

  3)選中剛粘貼的classes.jar檔案,右鍵,選擇 Add as Library,出現一個彈視窗,預設選中mysdk子產品,直接點确定

【Unity與Android】01-Unity與Android互動通信的簡易實作
  然後,這個classes.jar檔案就被引入到工程中了,展開三角,可以看到如下三個子產品,其中就有com.unity3d.player。如下
【Unity與Android】01-Unity與Android互動通信的簡易實作

 4、建立本子產品的Activity檔案

  1)展開mysdk子產品下的src目錄,選中com.leitui.mysdk,然後右鍵,建立一個Activity,選擇Empty Activity,輸入Activity Name以及Package Name和Language,勾掉Generate Layout File, 完成。
【Unity與Android】01-Unity與Android互動通信的簡易實作
 建立的SDKMainAcivity.java腳本,預設繼承自AppCompatActivity,并帶有一個onCreate方法,如下:
【Unity與Android】01-Unity與Android互動通信的簡易實作

現将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)
//---------------------------指令結束------------------------------      
【Unity與Android】01-Unity與Android互動通信的簡易實作

 這組指令的功能就是打包方法1的自動化版。

 build.gradle檔案修改後,會提示要求同步,直接同步即可。同步結束,在IDE右上點開Gradle視窗,在other下找到exportJar指令。

【Unity與Android】01-Unity與Android互動通信的簡易實作

 輕按兩下exportJar指令,等一會就會自動生成本子產品的jar檔案了。

生成的檔案位于mysdk/release/目錄下,如下圖

【Unity與Android】01-Unity與Android互動通信的簡易實作
6、修改AndroidManifest.xml檔案
  1)打開本子產品的AndroidManifest.xml檔案,檔案位于mysdk/src/main目錄下,如下圖:      
【Unity與Android】01-Unity與Android互動通信的簡易實作
  這個檔案的内容,目前隻有一對application标簽和activity标簽 
【Unity與Android】01-Unity與Android互動通信的簡易實作
  删掉這三行,将其内容修改為如下: 
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目錄下

  完成之後檔案結構圖如下:

【Unity與Android】01-Unity與Android互動通信的簡易實作
  libs目錄用于存放其它android插件的jar檔案,沒有也可以不用建立。

 2、制作一個UI界面

 a.在SampleScene場景中建立一個Canvas,并建立一個名為"receiveObj"的對象,在receiveObj之下再放一個按鈕和一個Text。

 按鈕用于觸發調用Android方法。

 Text用于顯示Android調用Unity方法傳遞來的參數。如下圖:

【Unity與Android】01-Unity與Android互動通信的簡易實作
 這裡要注意,receiveObj的名稱必須與SDKMainActivity類的AndroidCallUnity方法中的UnityPlayer.UnitySendMessage方法的第一個參數保持一緻。
【Unity與Android】01-Unity與Android互動通信的簡易實作
 b.建立一個SDKTest.cs檔案,将腳本挂在receiveObj對象上。如下: 
【Unity與Android】01-Unity與Android互動通信的簡易實作
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通信成功。

  示範見下圖:

調用 前:

【Unity與Android】01-Unity與Android互動通信的簡易實作
,調用後: 
【Unity與Android】01-Unity與Android互動通信的簡易實作

五、一點說明

1、子產品的包名和Unity的Bundle Identifier可以不一緻(至少在Module模式下,是可以不一緻的)。

說明:寫這條是因為其它相關文章全都要求兩邊保持一緻,而如果子產品的包名要跟着Unity工程走,也太蛋疼了,是以驗證了下。

2、将AndroidManifest.xml引入到Unity之後,在unity中設定的Product Name就無效了。

需要在AndroidManifest.xml的application标簽中,添加android:label屬性來指定。

  

      初次接觸Android開發,以上内容如有錯誤,還請不吝指出。

  本檔案所使用的Android工程和Unity工程源碼在此:安卓工程  Unity工程