使用Unity也有不短的时间了,安卓包也打过很多,可是对Unity与Android的交互却知之甚少。java
因工做需求,须要在Android平台接一些sdk(扩展功能)。我就借此机会了解了下Unity与Android交互的一些知识,并作了一个简易实现。android
现将实现步骤记录下来以供往后查阅。后端
Unity、Android Stuido以及JDK安装,这些都是基本操做了,网上也有不少教程,这里不细述。app
本次开发所使用的软件版本以下:ide
Android Studio 3.5学习
Unity 2018.3.10f1测试
Java 1.8.0191gradle
要在unity项目中进行安卓功能扩展,有两种方式:ui
一、Unity项目导出为Android工程,而后在Android Studio(之后简称为AS)中进行二次开发,添加扩展功能。这样的方式开发起来很灵活,改动起来也很方便,可是就是很麻烦,由于每次改动都要打一回安卓工程。this
二、将扩展功能制做成Android库文件(jar包),而后将jar包导入到Unity中,直接使用。这样的方式,使用者没法修改这个库文件的功能,但也更便于使用 。
由于我将要作的功能可能会在团队内传播使用,也不须要每一个人都去作改动。所以选择第二种方式。
因此本文的目的就是:
制做一个Android库文件(jar包),而后在Unity中使用它。
一、新建Android工程
打开AS,新建一个Android工程,选择Empty Activity,配置工程名称、包名、位置以及语言,以下图
语言最好选Java,由于Java和C#的语法极为类似,学习成本很低。
工程建立以后,默认显示的是Android视图下app的工程结构,以下图。能看到在包名下有一个MainActivity.java的文件,这是安卓app的入口,不过这里并不打算使用它,忽略便可。
二、建立一个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模块进行操做。
三、引入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。以下
四、建立本模块的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 }
五、将模块打包
打包方法有两种。
一是手动进行构建,而后在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/目录下,以下图
六、修改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工程。
a.新建一个Unity工程,在Assets目录下新建Plugins/Android/bin目录。
b.将第三步修改的AndroidManifest.xml文件拷贝到Assets/Plugins/Android目录下
c.将第三步生成的mysdk.jar文件拷贝到Assets/Plugins/Android/bin目录下
完成以后文件结构图以下:
libs目录用于存放其它android插件的jar文件,没有也能够不用建立。
二、制做一个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的实例,还有待探究。反正目前这么写了就好了。三、打包并测试。
在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的Bundle Identifier能够不一致(至少在Module模式下,是能够不一致的)。
说明:写这条是由于其它相关文章全都要求两边保持一致,而若是模块的包名要跟着Unity工程走,也太蛋疼了,因此验证了下。
二、将AndroidManifest.xml引入到Unity以后,在unity中设置的Product Name就无效了。
须要在AndroidManifest.xml的application标签中,添加android:label属性来指定。
初次接触Android开发,以上内容若有错误,还请不吝指出。