前言:
Unity和android交互,这个问题,在百度搜一下,解决方案还是蛮多的,起初我也是照着百度出来的帖子一步步实现了.但是!大部分帖子只管功能实现根本不讲缘由,且实现的功能比较死板.看完这样的帖子,对学习完全没有帮助啊喂!想要在项目中使用交互代码相应的也会遇到许多问题(后面会说遇到的问题,以及解决方案).
适用人群:
1.unity开发者,不会使用AndroidStudio,通过本教程,可以了解Android Studio的基本使用.
2.搜索了很多unity,Android交互文章却没找到合适解决方案的同学.
使用jar还是aar进行通信?
Jar中包含了原生java代码,如果我们只是测试通信,一个简单的jar包就能够通信,然而实际开发中,一般不可能只用一个jar包,在android工程中一般都会引用很多jar包,
aar大概是设计出来用来替代jar来进行通信的,不仅包含了jar 还包含了提供的android资源(图片,布局文件等).这也是aar包比jar大的原因,我们可以通过删除不相关内容来降低aar包的大小.
AndroidStudio:
打开AndroidStudio- NewProject:
新建activity之后,要在activity中引用unity的方法就需要引入unity的jar包,根据以下目录找到unity提供的jar:
根据在unity中选择的打包方式,复制出来文件夹中的jar包
放在android studio中的libs文件夹中
这个classes.Jar是unity给安卓写的脚本,在android中引用了这个jar包,就可以调用里面的方法(各位原生老哥应该比我清楚....),右键classes.jar 最下面有个add as a Library 或者
导入成功后,就可以引入unity提供的package了
根据我想实现的需求,需要引用两个package:
1. 在安卓原生中调用unity方法(向unity传递数据),import com.unity3d.player.UnityPlayer
2. 为了unity中能找到并调用andorid方法( import com.unity3d.player.UnityplayerAcitvity,来继承UnityPlayerActivity)
对默认的activity进行以下更改:
Android Studio端 逻辑代码编写完毕.这里讲解下unity android相互调用API的使用规则:
Android调unity:
unitySendMessage需要三个参数:s1(string):unity 场景中的的gameobject的name .s2(string):该gameobject上的脚本上的方法名 s3(string):传递的消息内容,可以是json,xml之类的.也就是说unity通过寻找场景中的gameobject,调用unity自己的sendmessage来传递消息(这里使用的是Unity提供的unitysendMessage往unity回调数据,它是通过反射实现的,这个帖子//www.greatytc.com/p/f5b20d43315a提供了java中反射的实现,可以不用unity的方法直接实现通信).
Unity调用android:
Unity官网提供的死代码以及网上一半帖子的代码如上,大概意思是通过包名找到了java类, 但是是具体哪一个类的对象需要用”currentActivity”获取到.这里我推测我们在android 中新建的MainActivity是继承了UnityPlayerActivity才能被以上两行代码找到(这个必须要继承UnityPlayerActivity 才能被找到 很鸡肋很鸡肋).
接下来导出aar包:
Android Studio默认build会生成apk,此时需要更改build.gradle来让Android studio导出aar包
设置完毕后,点击build-RebuildProject进度走完后,打开build/outputs/aar找到导出的aar
右键,show In Explorer在文件夹中显示它
可以看到它是可以被解压软件打开的,我们可以打开看下,来删除那些我们不需要的内容
由于在Android Studio我无脑build导致build出来一大堆用不到的东西,我又不知道如何在Android studio中不导出这些,所以现在直接操作aar删除,首先要分辨出来哪些是有用的,哪些是无用的.
有用的:我写的两个java类,猜测被自动打入到aar包中外层的Classes.jar中了
无用的:事先从unity导入的jar包(存放在libs/classes.jar,至于它为啥和aar中的classes.jar名字一样,我也不知道),布局文件,dpi文件 删除完毕后,如下
Unity中:
在unity的 Assets目录下新建Plugins/Android(Plugins文件夹必须是在Assets根目录) 文件夹 拖入刚才的aar和AndroidManifest(此处应该有疑问,为什么aar包中有AndroidManifest文件,还要在unity的目录下还放置一个AndroidManifest)
在unity中新建name为AndroidCall的gameobject以及新建脚本,新建方法TestCall.
unity playersettings中packageName和Android activity的package要保持一致
大部分帖子写到这里展示一下调用成功的截图,就结束了帖子...前面讲到的很鸡肋的unity获取Android 对象的方式,就这样鸡肋下去了,准确的来说,目前实现的unity和Android交互只是一个最基础的demo,在实际项目开发过程中根本不可能使用这种单一的方式.我相信这些朋友后面也都发现了它的缺点,一部分网友更新了unity Android交互第二篇...,我认为要想得心应手的使用unity和底层通信就要了解其通信原理,但是在介绍通信原理之前必须要介绍一下AndroidManifest这个东西,它对我们理解如何通信至关重要.
AndroidManifest是什么?他有什么作用?
清单文件(Mainfest)普遍存在于各个平台生成的项目中,是对指定内容的一种描述,提供了指定内容构成有关的所有信息,且有唯一标识作用,就像是一条http请求,header包含了对body体内容的描述.
一般由以下几部分构成:
1.指定内容的版本信息.
2.指定内容对其他内容的依赖
3.指定内容的安全权限
AndroidManifest是Android中打包时生成的一个清单文件,位于根目录:
1.包含了app要在Android系统上运行的基础信息:app图标(Icon),名称(product Name),横屏还是竖屏(orientation),版本号(version)等等,在unity中是通过PlayerSettings的可视化界面配置app的基本信息的.这些内容会在打包的时候写入AndroidManifest中,有了这些基础设置,该app安装在Android设备上才会在桌面中显示出来.
2.设置程序入口.有了上一条说的基础配置之后,虽然能够在Android中显示,但是没有设置要启动的Activity,会直接闪退.(平时正常打包出来的app能运行是因为已经设置好了程序入口Activity),在Android中应该是允许配置多个activity,设置一个mainActivity,然后在activity中切换.unity打的Android只使用一个activity.
3.配置该app需要的Android功能:比如app需要开启摄像机或者麦克风,需要在Manifest中配置权限,否则Android不会弹出权限请求,权限部分需要<uses-permission>标签,再比如需要Android广播接收器功能就需要配置<receiver>标签.
Android开发的同学都知道,每当新建一个Activity ,就会生成一个Manifest文件,它用来指定当前activity需要的各种配置.然后一个activity就可以通过设置gradle 来生成一个apk,运行在Android设备上,那unity是如何生成apk的呢?我们新建一个unity工程并导出成Android Project来研究一下:
新建一个空的unity工程,将unity 的buildSettings上的export project勾选就可以将项目导出成Android project
使用AndroidStudio打开该工程发现,unity的export过程就是将unity的内容导出成了一个activity,叫做UnityPlayerActivity,这个activity的packageName就是在unity的Android PlayerSettings中设置的packageName
前面说到每个activity都包含Manifest文件,打开Manifest文件如下
Unity打包出来的Manifest文件是根据存放在unity安装目录下的Manifest模板生成一个com.XXX(unity中设置的package).UnityPlayerActivity,所以我们unity的apk在Android设备中是通过Manifest中设置的启动项Activity启动的.
下面回过头来看刚才那个测试demo的Manifest文件:
主要功能就是设置了com.axin.mylibrary.customActivity这个activity为启动activity,已知unity会自动生成UnityPlayerActivity这个activity作为启动项,如何把咱们的activity替换到Unity生成的Manifest文件中呢?官方文档对此进行了说明:https://docs.unity3d.com/2017.4/Documentation/Manual/android-manifest.html?tdsourcetag=s_pctim_aiomsg
在unity中AndroidManifest文件的合并重点是这一句(已使用谷歌翻译)
在unity中提供的AndroidManifest文件只能有一个,它的目录是Assets/Plugins/Android/AndroidManifest.xml,这也是我们前面在unity目录下导入AndroidManifest的原因,下面展示下unity替我们合并的Manifest文件
对比之前打包的空工程的AndroidManifest文件,启动项activity已经变成了.customActivity,这就是我们在assets/plugins/Android目录下放置的AndroidManifest文件中的内容覆盖了unity的fest文件模板生成的效果.
由于unity的playersettings中设置的packageName和 aar包中Android activity的packge一致(此处应该有疑问,我unity中的packageName应该指定的是我司的公司名称和项目名称,怎么能和第三方aar保持一致呢?也不能我用了阿里巴巴的aar,我就要让我unity项目的packageName带上阿里巴巴吧?),所以启动项activity就是com.axin.mylibrary.customactivty,另外customactivty又是继承UnityPlayerActivity的
所以我们的代码
获取的就是这个java类的对象(为了能用上述两行代码找到我们定义的activity,实际上是我们的activity抢占了unity默认的启动activity,这会带来不同程度的后果,但是如果我们没有使用UnityPlayerActivity这个父类的需求的话,我们尽量不要继承它实现通信,下文会提到另一种调用javaclass对象的方法.)
此时调用AndroidCallUnity方法就会调用unity中的方法了.
以上简单的解释了下我们的通信demo是如何运行的,但是它仅仅是demo,下面我们分析一下实际项目的需求:
A.在unity中接入一个或者多个第三方sdk,意味着导入多个aar.
B.在Android原生中接入多个原生sdk,然后将数据传回unity.
C.Unity项目最终只作为一个activity来运行,如果各个aar都要抢占Activity入口,都要求unity项目packageName和自己保持一致那该怎么办?(上文的测试demo抢占了Activity入口且要求项目packageName和aar的保持一致.网上搜索的unity Android通信帖子大部分都这样写的)
情况A多为unity开发者遇到,意味着unity中需要导入多个aar,就可能(我觉得导出的aar都会有Manifest文件,因为我也不是Android开发,不敢说太绝对)会附带多个Manifest.多个AndroidManifest可能存在于unity项目工程中,也可能存在于aar包中.
对于Unity工程里的AndroidManifest:
众多AndroidManifest可能拥有不同的系统权限或不同的APILevel等.这时候就应该合并不同的Manifest.前面说到unity的项目中只能存在一个AndroidManifest(起初我以为AndroidManifest只要放在Android目录下就可以,unity中的editor文件夹,lib文件夹貌似都是这个逻辑,觉得多个Manifest文件unity会帮我们合并,事实上只要assets/Plugins/Android下的fest文件才能被合并,害我在这里卡住了很久),所以就需要手动合并这些Manifest,只保留一份在Plugins/Android目录下.保证手动合并的Manifest中只有一个MainActivity.且这个activity尽量不主动设置,让unity去生成默认的就可以.合并方法是复制粘贴....,即把非指定路径下的Manifest文件中和指定路径下Manifest文件不同的东西复制过去...比如权限什么的,最终文本还是要符合xml结构的.
对于存在于aar包中的Manifest文件:
打包过程中会根据谷歌的固定算法进行合并,unity打包apk的流程大概是先打成Android 工程,再利用Android中的Gradle的Task打出apk,谷歌的合并算法应该是在Task里,和unity没什么关系,情况B和这个情况类似,AndroidStudio也是依赖Gradle打包的.关于AndroidStudio中Manifest的合并原理://www.greatytc.com/p/0febcb3625d9
AndroidManifest总结:
根据分析unity中export的Android project分析发现,unity是根据Unity的存放目录下的AndroidManifest模板和Assets/Plugins/Android/AndroidManifest.XML 合并生成一个AndroidManifest,作为unityPlayerActivity的配置文件.unity项目目录下的fset文件和aar包中的fest文件不同之处在于,unity中的fest通常抢占了入口Activity或对unity默认Activity进行了一些修改,而aar包中的fest文件通常只包含该aar需要的系统权限和API Level ,该部分会在Gradle的task中被谷歌算法合并.
情况C的解决方案:
1.如果接入的第三方sdk都是原生Android Sdk,则可以在Android Studio中新建一个和Unity工程packageName一样的Activity,整合众多sdk要使用的API在一个Activity里,然后这个Activity设置为Main入口,放置新的AndroidManifest文件在unity指定文件夹下,下面这个教程贴是这么做的:https://blog.csdn.net/LIQIANGEASTSUN/article/details/78805902
2.不使用activity,直接使用Java类,Unity中可以通过new AndroidJavaObject(“包名”)直接生成一个java对象,来调用其中的方法(直接继承UnityPlayerActivity主要是方便查找到activity ),各个sdk自己各自维护一个java类,提供unity调用的方法,在unity中通过unity的方式直接new这个java类来调用(需要提供包名+类名),在unity中整合各个第三方sdk,此方案不需要在AndroidStudio中新建activity,因为不需要抢占activity所以也不需要在unity文件夹下放置AndroidManifest,打包过程中会自动合并aar包中的fest.
步骤如下:
首先,在AndroidStudio中新建一个java类:customSendMessage
导入unity中包含sendmessage的package(如何导入在上文) ,写一个AndroidCallUnity 方法
将aar导出后直接放到plugins/Android目录下,(导出步骤在上文),在unity中
在java中有可能要用到上下文,这里的上下文(什么是上下文可以百度搜一下,大概指的就是当前运行的程序域吧)指的是当前的Activity,获取当前activity 返回给java
文章完毕,如有错误,还请各位大佬纠正,我会第一时间进行修改,谢谢大家.
我的CSDN地址:
https://blog.csdn.net/weixin_39106746/article/details/103469620