Unity3D与Android通信
Unity3D与Android平台通信
分为两种,一种导入jar包,一种是导入aar包。
jar包方式
什么是jar包
jar包的全称是Java Archive File,它是一种压缩文件格式,和zip格式兼容,与zip格式不同的是它包含一个META/INF/MANIFEST.MF的清单文件,这个是jar包生成过程中自动创建的,非常的关键,主要是用来设置执行入口类和支持库的路径,比如:
Manifest-verion:1.0 Class-Path:./lib/msbase.jar ./libs/mysql.jar Created-By:1.6.0(sun Microsystem inc.) Main-Class: HelloWorld
其中Class-Path比较容易被忽略,它的作用是用来设置支持库的路径。上述例子中当前目录下lib文件夹中的几个jar包都能被运行时找到。关于这块内容的一个简单解释可参考:https://blog.csdn.net/Jungle_hello/article/details/51235331
jar包中的主要类容是类文件,还有其他的文件,比如jsp文件,html文件,图片等,所以简单的来说jar包就是把你写的代码打包成的一个压缩文件。
jar包的作用
jar包的一个用途就是作为程序使用,但和平时我们理解的二进制程序不同,这个程序是使用运行时解释执行的,执行这种程序的命令通常为:java xxx.jar,而另一个用途就是代码重用,即把我们自己写的代码打包成一个jar包给他人使用。具体的使用教程可自行google。
那在Unity3D中我们如何导入jar包呢?首先我们先使用android studio创建一个jar包。
创建jar包
android studio中默认打包成aar包,如果想要eclipse打包jar包的功能,需要自己定制。
首先使用默认的模板创建一个新工程,在工程中右键项目New-》Module
选择Android Library
设置模块名称后和最低支持android SDK版本后,直接finish。若果创建之后想删除则右键项目-》Open Module setting,选择模块
创建好模块之后,接下来我们有个可选的步骤,就是添加外部的jar包,我们先介绍最简单的不引用其它jar包的方法。首先添加一个新类,方法是切换到android视图,右键该模块下java文件夹中第一个子文件夹,选择new-》class,填写类名称后确定即可。
新建的类中我们填写一个简单的方法,代码如下:
package com.androidaddin.androidlib; import android.util.Log; public class Helper { public static String HelloWorld() { return “hello world”; } }
之后我们构建模块,如图:
这样的话我们会生成一个aar的包,把aar包解压(直接把后缀改为zip),我们可在其中找到classes.jar的包,这个jar包即我们想要的jar包。
除此之外,可以自己写gradle的方法,自定义生成jar包,方法是打开module中的build.gradle文件,添加如下方法:
//添加生成jar的方法 task buildJar(type:Jar){ baseName'lib' //jar包命名 from('build/intermediates/classes/debug/com/example/administrator/myapplication') into('com/example/administrator/myapplication') exclude('BuildConfig.class','R.class') //去除无用的资源类及build资源类文件 exclude{it.name.startsWith('R$');} } //添加清除jar包的方法 ask cleanJar(type:Delete){ delete'build/libs/lib.jar' } //将library的build任务和clean的任务添加到jar包的大包任务之前: buildJar.dependsOn(cleanJar,build); //取消打包过程中的错误检查打断: android{ lintOptions{ abortOnErrorfalse } }
参考网址:https://www.jianshu.com/p/756693ee7e6e
向Unity3D中导入jar包
新建Unity3D工程,添加Plugins/Android的文件夹,把我们生成的classes.jar(文件名不一定是classes)文件添加到这个文件夹中,然后新建一个脚本,在start中添加如下方法:
void Start() { using (AndroidJavaClass jc = new AndroidJavaClass("com.xin.yichen.unitytest.utest")) { string content = jc.CallStatic<string>("HelloWorld"); Text t = GetComponentInChildren<Text>(); t.text = content; Debug.Log(content); } }
然后在场景中添加一个Text(右键->create->UI->Text),把这个方法挂在Canvas物体上,保存,然后发布到手机运行即可(因为jar包需要java运行时来执行,在editor中运行获取android对象,所以执行会报错)。
但在Unity3D中,我们需要的不是这么简单的一些功能(这些功能我们完全可以使用c#中实现),而是与android当前显示unity3d渲染内容的activity通信,这个怎么做呢?在这里需要一个关键的jar包——Unity3D打包的一个jar包。我们需要把这个jar包引入到我们的模块中,接下来我们就来看看如何把一个jar包引入到我们的模块当中。
首先找到Unity3D的jar包,其位置在:
(Win path) unity3d的安装路径\Unity\Editor\Data\PlaybackEngines\androidplayer\bin\classes.jar
(Mac path) Unity.app(show packages)Applications\Unity(rightclick ShowPackageContent)PlaybackEngines\AndroidPlayer\Variations\mono\Release\Classes\classes.jar
复制这个jar到模块的libs文件夹下,然后右键jar包-》add as library。
这样即导入了jar包,结果如下图所示。
这个jar中包含了Unity3D针对Android平台封装的UnityPlayerActivity的类,这个类我们接下来要关注的类。
首先我们切换到Android视图,右键模块下java文件夹下的第一个字文件夹-》new-》activity-》Empty Active,创建一个空Acitivity。然后让它继承UnityPlayerActivity类,同时删除布局代码和布局文件(不需要)。
删除布局代码和布局文件
然后添加我们的方法。比如这里我们添加了一个add的方法来计算两个数的和。之后我们就还剩下一个工作,修改AndroidMenifest.xml文件。
我们可以简单的把工程下的androidmanifest.xml拷贝过来,修改。标为红色的表示错误,我们把它删除,包名和App的名字按你自己的需求设置。在activity标签中最后添加一行:<meta-data android:name="unityplayer.UnityActivity" android:value="true" />。
修改后如下图:
然后选择build-》make module “xxx”,会生成aar包。在这里可以有两种方式提取jar包,第一种还是把aar包解压了,提取其中的classes.jar、libs、res文件夹。需要注意的是,这一次我们引用了Unity3D的jar包并继承了它,所以不能像之前一样简单的把classes.jar放入Plugins/Android下,不然会报:D8: Program type already present: com.unity3d.player.Camera2Wrapper的错误,也就是重复文件。这个问题产生的原因也很简单,就是Unity3D发布APK的时候走的还是Android发布的那一套,gradle或ant,不管哪一种,它都引用了之前我们拷贝的那个classes.jar这个jar包,也可以简单的理解为Unity3D会生成一个android工程,然后使用gradle或这ant发布,而Unity3D会把这个jar包放入这个工程的libs这个文件夹下。而如果我们在Unity3D中添加了Plugins/Android文件夹的话,它也会把这个文件夹合并到工程中。所以如果把classes.jar直接放到Plugins/Android下,就会造成工程目录下有一个classes.jar,libs下也就一个classes.jar,重复文件问题就出现了。解决办法很简单,把外层的classes.jar挪到libs下替换到原先的classes.jar(这个classes.jar其实就是Unity3D的那个,所以我们用自己新的这个替换它),然后把libs、res、AndroidManifest.xml拷贝到Plugin/Android下即可。
这里还有一个要注意的地方是AndroidManifest.xml包名的修改,要求拷贝到Plugin/Android的AndroidManifest.xml的包名要与Unity3D发布时的包名一致,即下图中的package name。
这里如果使用Android模块的包名发布出错的话,可以考虑换个包名,但要保证AndroidManifest.xml中的包名和这里的一致。
在Unity3D中新建脚本,代码如下:
void Start() { using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity"); Text t = GetComponentInChildren<Text>(); t.text = jo.Call<int>("add",10,10).ToString(); } }
Unity3D中通信我们可以简单的使用其封装的AndroidJavaClass和AndroidJavaObject两个类来操作,它们是对JNI操作的封装,想了解细节的话可以到Unity3D的官方文档查阅。
其实这里也可以不用解压aar,自己拼一个jar出来。首先我们在Android Studio中切换到Project视图,进入模块目录下的src/main文件夹下,复制res和AndroidManifest.xml文件,然后到模块目录下build/intermediates/packaged-classes下找到libs和classes.jar,同样的拷贝外层的这个classes.jar放入libs文件中,然后把这个libs连同之前的res和AndroidManifest.xml导入Unity3D中即可。
在改AndroidManifest.xml之后可能会遇到一个问题,app莫名的崩溃。原因是activity名称路径不对,我拷贝工程的AndroidManifest.xml内容修改之后的结果如下:
activity的名称是”.MainActivity”,这个“.”表示包名,也就是说如果我的包名是com.xxx.unitytest,则它会被解释为”com.xxx.unitytest.MainActivity”,如果我们没有更改包名,这个是没有问题的,如果我们在Unity3D的AndroidManifest.xml修改了包名,则Activity会找不到入口类,所以崩溃。解决方案,在这里使用全名即可。
这个问题的出现是因为对AndroidManifest.xml不了解,所以我们简单的来了解一下AndroidManifest.xml。
AndroidManifest.xml可以理解为应用程序和Android系统之间的接口,任何一个应用程序都有这个文件。它用来向系统描述应用程序的信息。比如一个典型的AndroidManifest.xml内容如下:
其中uses-sdk标签说明应用程序需要的android sdk的版本,Application标签是关于应用程序的详细信息,如Android:icon表示应用程序的图标,Android:label表示应用程序的显示名称,android:theme表示使用的UI主题,activity标签表示活动,其属性android:name表示activity的类名,intent-filter表示活动的意图,action的名称设置为android.intent.action.MAIN表示这个活动被设置为应用程序的入口,category设置为”android.intent.category.LANUCHER”表示应用程序可通过设备启动器的图标来启动。
aar包方式
导入aar包和导入jar包的流程一样,不一样的只是aar包和jar包。我们先了解一下aar包和jar包的区别,主要的区别就是aar包可以附带资源文件,比如图片和res中的所有文件。
创建aar包
创建aar包的过程已在jar包的创建过程中介绍,即Android Studio默认就是创建aar包,我们新建模块后直接build->make module xxx即可。aar包可在模块目录下(project视图)outputs/aar下找到。
Unity3D导入aar包
在拷贝aar包之前,还需要处理一下这个aar包,即把libs外的classes.jar诺到libs里替换libs里的那个classes.jar。在window下可以使用解压软件打开aar包,直接操作,在mac下,需要修改aar包的后缀为zip,然后解压,修改后再使用zip命令或者jar命令再次打包为aar包:
zip –r path/my-aar-lib.aar ./* jar cvf my-aar-lib –C ./ .
与使用jar包流程中的操作一样修改AndroidManifest.xml,让后把aar包和AndroidManifest.xml宝贝到Plugins/Android中即可。Unity3D的调用代码相同。
Android向Unity3D发送消息
Android向Unity3D发送消息使用UnityPlayer的UnitySendMessage即可,示例:
UnityPlayer.UnitySendMessage("Main Camera","messgae",edit.getText().toString());
其中第一个参数“Main Camera"是Unity3D场景的物体,“message”是方法,后面的是传给这个方法的参数。我猜测这个UnitySendMessage的方法是调用了Unity3D中GameObject.SendMessage的方法。
结语
Unity3D与Android通信的重点就是要理解jar包和aar包的原理和Unity3D中如何合并工程中Plugins/Android文件夹中的内容,jar包和aar包可以理解为代码库,而Unity3D对于Plugins/Android文件夹中内容的合并就是替换,即使用用户的Android代码库和资源替换Unity3D默认的代码库(classes.jar、AndroidManifest.xml),理解这两个再来看它们之间的通信就显得不那么神秘了。
参考资料:
https://blog.csdn.net/u010377179/article/details/53105062
https://blog.csdn.net/ChinarCSDN/article/details/80012456