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包都能被运行时找到。关于这块内容的一个简单解释可参考:

  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 第三方jar包 jar包可以在安卓上运行吗_jar包

  选择Android Library

android 第三方jar包 jar包可以在安卓上运行吗_jar包_02

  设置模块名称后和最低支持android SDK版本后,直接finish。若果创建之后想删除则右键项目-》Open Module setting,选择模块

 

android 第三方jar包 jar包可以在安卓上运行吗_android 第三方jar包_03

 

  创建好模块之后,接下来我们有个可选的步骤,就是添加外部的jar包,我们先介绍最简单的不引用其它jar包的方法。首先添加一个新类,方法是切换到android视图,右键该模块下java文件夹中第一个子文件夹,选择new-》class,填写类名称后确定即可。

  新建的类中我们填写一个简单的方法,代码如下:

package com.androidaddin.androidlib;
import android.util.Log;
public class Helper {
public static String HelloWorld()
{
return “hello world”;
}
}

   之后我们构建模块,如图:

android 第三方jar包 jar包可以在安卓上运行吗_jar包_04

 

  这样的话我们会生成一个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

android 第三方jar包 jar包可以在安卓上运行吗_java_05

 复制这个jar到模块的libs文件夹下,然后右键jar包-》add as library。

android 第三方jar包 jar包可以在安卓上运行吗_android 第三方jar包_06

  这样即导入了jar包,结果如下图所示。

android 第三方jar包 jar包可以在安卓上运行吗_游戏_07

 

  这个jar中包含了Unity3D针对Android平台封装的UnityPlayerActivity的类,这个类我们接下来要关注的类。

首先我们切换到Android视图,右键模块下java文件夹下的第一个字文件夹-》new-》activity-》Empty Active,创建一个空Acitivity。然后让它继承UnityPlayerActivity类,同时删除布局代码和布局文件(不需要)。

android 第三方jar包 jar包可以在安卓上运行吗_java_08

   删除布局代码和布局文件

android 第三方jar包 jar包可以在安卓上运行吗_jar包_09

android 第三方jar包 jar包可以在安卓上运行吗_java_10

 

  然后添加我们的方法。比如这里我们添加了一个add的方法来计算两个数的和。之后我们就还剩下一个工作,修改AndroidMenifest.xml文件。
我们可以简单的把工程下的androidmanifest.xml拷贝过来,修改。标为红色的表示错误,我们把它删除,包名和App的名字按你自己的需求设置。在activity标签中最后添加一行:<meta-data android:name="unityplayer.UnityActivity" android:value="true" />。
  修改后如下图:

android 第三方jar包 jar包可以在安卓上运行吗_android 第三方jar包_11

   然后选择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 第三方jar包 jar包可以在安卓上运行吗_jar包_12

  这里如果使用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内容修改之后的结果如下:

android 第三方jar包 jar包可以在安卓上运行吗_java_13

 

  activity的名称是”.MainActivity”,这个“.”表示包名,也就是说如果我的包名是com.xxx.unitytest,则它会被解释为”com.xxx.unitytest.MainActivity”,如果我们没有更改包名,这个是没有问题的,如果我们在Unity3D的AndroidManifest.xml修改了包名,则Activity会找不到入口类,所以崩溃。解决方案,在这里使用全名即可。

这个问题的出现是因为对AndroidManifest.xml不了解,所以我们简单的来了解一下AndroidManifest.xml。

  AndroidManifest.xml可以理解为应用程序和Android系统之间的接口,任何一个应用程序都有这个文件。它用来向系统描述应用程序的信息。比如一个典型的AndroidManifest.xml内容如下:

android 第三方jar包 jar包可以在安卓上运行吗_游戏_14

   其中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 第三方jar包 jar包可以在安卓上运行吗_游戏_15

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://xinyustudio.wordpress.com/2015/12/31/step-by-step-guide-for-developing-android-plugin-for-unity3d-i/