Unity Android平臺下插件/SDK開發通用流程

本文主要面向對Android開發不甚瞭解的Unity開發者,介紹了基於最新的Android Studio的標準Android開發環境與項目結構的配置流程,在此基礎上,開發者可以快速的進行SDK的接入與插件的開發。

目前國內各大博客上搜到的文章內容基本上都是相互抄來抄去,比如說先是清一色的繼承UnityPlayerActivity,然後再配置AndroidManifest文件等等,這些確實有效果,但是具有很大的侷限性。例如,如果項目中如果存在兩個Android插件或SDK會怎麼樣?都繼承UnityPlayerActivity顯然是不行的,啓動入口Activity只能有一個,AndroidManifest文件沒法這樣配置。筆者最近在做畢設,幫同學接了若干個Android下的SDK,包括支付寶,科大訊飛以及二維碼掃描的SDK,參考一些國外的優秀博客以及自己的實踐,總結出一套SDK接入與插件開發的通用流程,在這裏分享給大家。


開發環境

1.開發軟件:筆者使用的開發軟件是Unity 5.4.3f 和Android Studio 2.3。

2.所需類庫:UnityPlayer等需要的classes.jar包。在 Unity支持Android下IL2CPP後,UnityPlayer相關的這個jar包位置由原來的`Editor\Data\PlaybackEngines\AndroidPlayer\bin`變成了`Editor\Data\PlaybackEngines\AndroidPlayer\Variations\mono(or il2cpp)\Release\Classes`,選擇jar包時需要注意選擇對應Backend的版本。


項目構建

1.啓動Android Studio新建一個Project,會彈出創建工程嚮導,這裏可以隨意填寫Package NameMininum SDK,最後讓選擇的Activity類型也可以隨意。因爲我們並不會用到現在創建的這個項目,我們只是在其結構上再創建一個Library作爲我們的Plugin。這裏值得注意的是,根據你填寫的Company domainApplication name生成的Package name總是小寫的,點擊右側的Edit按鈕可以對其進行修改。

2.創建項目點擊Finish後進入編輯器界面,空無一物,點擊左側的Project,然後切換至Project視圖,如圖下圖左所示。隨後在項目名稱上點擊右鍵選擇New -> Module來創建一個新的模塊,這時新的創建嚮導會啓動,選擇Android Library類型,隨後輸入包名的配置與Mininum SDK,注意這裏的Package Name需要和Unity項目中的Bundle Identifier保持一致(包括大小寫),保持一致的原因是Unity在最後打包時會將所有插件裏的AndroidManifest.XML進行合併,如果包名不一致就會出錯;而Mininum SDK這裏需要在Android 3.0(API 11)以上,因爲隨後會用到Android 3.0之後才支持的Fragment特性,最後點擊Finish完成創建,項目結構應該如下圖右所示,紅框部分是新創建的Library。

                     

3.在新的Library創建完成後,需要刪除app相關的內容。點擊菜單欄File -> Project Structure,選中左側的app然後點擊上方的`-`號,最後點擊OK,稍等一會兒,Gradle Build會完成構建(可以注意到每次進行一次大的操作如增刪,Android Studio都會編譯一下項目,所以構建的時候請坐和放寬)。回到Project視圖,右鍵點擊app,選擇刪除。展開Library目錄如下圖中所示,以後所有的開發操作都會在創建的這個Library下面進行。下面對項目的結構進行一些說明。build/outputs/aar/文件下面是構建生產的供Unity使用的aar文件(aar文件和jar文件類似,Unity可以識別);libs文件夾下面是項目以來的類庫,可以說各種SDK的jar等等;最後是src文件夾,這裏面存放了AndroidManifest以及源碼。這些內容有一些是不需要的,例如一些單元測試的內容,我們刪掉測試工程以及res下面的Android自帶的資源及配置,最後的項目結構如下圖右所示,只保留了AndroidManifest.xml。
                

4.接下來是配置AndroidManifest的內容。雙擊打開AndroidManifest.xml,裏面有一些紅色的內容,那些是因爲剛纔各種刪除導致的,這裏不再需要常規的插入UnityPlayer等一堆東西,刪掉其餘不相關的內容後如下所示,可以看到內容非常精簡,只保留了最基礎的東西:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.soulgame.myplugin">
    <application android:allowBackup="true" android:supportsRtl="true"></application>
</manifest>

5.最後就是編寫我們的插件或者對接第三方SDK了。首先引入Unity的jar包,將classes.jar拷貝到文件夾裏libs文件夾下面,通用點擊File -> Project Structure,在左側選擇我們的插件,然後在上方選擇Dependencies,先刪除的現存的所有依賴庫,然後點擊`+`號 ->Jar Dependency添加Unity的jar包,點擊OK完成設置,稍等一會兒就完成了構建。對於第三方SDK的jar包,以同樣的方式進行引入。如果第三方依賴庫中有so文件,先不需要導入,稍後將會說明如何添加so文件。到這一步,項目、依賴庫都設置完畢。


編寫代碼

不要再繼承UnityPlayerActivity了!具體原因會在後面的參考資料裏給出。首先貼一段Android Studio裏的示例代碼,創建一個MyPluginClass類,這裏簡單的用到了一個static的Instance作爲這個類訪問的入口(類似單例),在Unity中只需要拿到Instance進行操作即可,裏面涉及到一些Android原生的Fragment的操作,具體含義可以Google一下:
package com.soulgame.myplugin;

import android.app.Fragment;
import android.os.Bundle;

import com.unity3d.player.UnityPlayer;

public class MyPluginClass extends Fragment
{
    private static final String TAG = "MyPlugin";
    private static MyPluginClass Instance = null;
    private String gameObjectName;

    public static MyPluginClass GetInstance(String gameObject)
    {
        if(Instance == null)
        {
            Instance = new MyPluginClass();
            Instance.gameObjectName = gameObject;
            UnityPlayer.currentActivity.getFragmentManager().beginTransaction().add(Instance, TAG).commit();
        }
        return Instance;
    }

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);  // 這一句很重要,保存對該Fragment的引用,防止在旋轉屏幕等操作時時丟失引用(Fragment隸屬於Activity)
    }
    //示例方法一:簡單的向Unity回調  
    public void SayHello()
    {
        UnityPlayer.UnitySendMessage(gameObjectName,"PluginCallBack","Hello Unity!");
    }
    //示例方法二:計算傳入的參數並返回計算結果
    public int CalculateAdd(int one, int another)
    {
        return one + another;
    }
}
編寫完成,將插件編譯導出,選中Android Studio右側的Gradle,選中如圖所示的選項編譯輸出,稍等一會兒,在左側的Project面板可以看到相關的aar文件了,這個aar文件就是最終輸出給Unity使用的aar包,拿到aar包之後,需要進行一項關鍵的操作,將其後綴改爲壓縮格式(zip或rar)打開壓縮包刪除掉libs/下面的classes.jar(原因是Unity在打包的時候會再次拷貝安裝目錄下的classes.jar到項目中造成衝突)!如果插件使用到了so文件,將對應平臺的so文件拷貝至libs下面,如下圖右所示:
        

最後將aar文件後綴改回爲aar拷貝至Unity工程中Plugins/Android文件夾下,不再需要AndroidManifest.xml
在Unity中編寫如下示例代碼調用插件的兩個方法:
using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class PluginManager : MonoBehaviour
{
    public string className = "com.soulgame.myplugin.MyPluginClass";
    public Text callbackText = null;
    public Text resultText = null;
    private AndroidJavaObject pluginObject = null;

    void Start()
    {
#if UNITY_ANDROID && !UNITY_EDITOR
        pluginObject = new AndroidJavaClass(className).CallStatic<AndroidJavaObject>("GetInstance", gameObject.name);
        pluginObject.Call("SayHello");                                                                               
        resultText.text = pluginObject.Call<int>("CalculateAdd", 22, 33).ToString(); 
#endif
    }

    public void PluginCallBack(string text)
    {
        callbackText.text = text;
    }
}
編寫完成後,設置好Platform和Bundle Identifier以及Mininum API Level(和插件保持一致),到真機上測試即可。

注意事項

1.UnityPlayer.UnitySendMessage()方法接受三個參數,每一個都不能爲null!如果不想填就用string.Empty。
2.AndroidJavaObject.Call()傳參的時候必須嚴格按照類型傳遞,如果參數是float卻填了一個int類型的也會造成調用失敗!
3.真機在打開開發者模式後可以用adb logcat -s Unity來獲取手機中Unity輸出的日誌,進而進行定位(adb需要在環境變量裏配置好),如下圖所示的日誌就說明找不到插件類裏對應的參數(筆者在操作的時候忘記填參數了):
4.以這種方式開發出的插件可以多個並存,例如將一些常用功能封裝成靜態類獲取電量,wifi狀態等,然後和支付寶,暴風魔鏡的SDK並存等等。
5.快速更改插件的包名,點擊齒輪並取消勾選Compact Empty Middle Packages,之後項目的結構中包名會變成三層結構,修改對應的結構即可:
         

參考資料

1.關於插件中繼承Fragment而不是UnityPlayerActivity的原因:Unity Android plugin tutorial 。
2.關於Android Fragment的資料:Fragment


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章