大家看过一些第三方组件的接入文档都知道,在Unity里面有几个特殊的文件夹是跟打包APK有关的。首先我们就来了解一下,这些文件夹里面的内容是经历了哪些操作才被放到APK里面的呢?
在Unity的Assets目录下,Plugins/Android无疑是其中的重中之重,首先我们先来看一个常见的Plugins/Android目录是什么样子的。
-Android
-- ApolloBase
-- ApolloPlugins
-- assets
-- libs
-- res
-- AndroidManifest.xml
后面的四个是Android工程的文件。前面两个文件夹是我们引用的第三方库,他们也会被打包到APK中。我们这个时候如果点进去前两个文件夹,我们会发现他们的目录结构跟Android这个目录也很像,大概是一下这个样子的。
-ApolloPlugins
-- libs
-- res
-- AndroidManifest.xml
-- project.properties
比较上下两层的目录接口我们可以发现有很多相似的部分,如:libs、res、assets文件夹以及AndroidManifest.xml文件。这些其实都是一个标准的Android项目的所需要的文件。Unity自带的Android打包工具的作用就是把上述这几个文件夹里面的内容以固定的方式组织起来压缩到APK文件里面。
接下来我们分别来看看Android打包工具都会做什么样的操作。
● libs文件夹里面有很多.jar文件,以及被放在固定名字的文件夹里面的.so文件。*.jar文件是Java编译器把.java代码编译后的文件,Android在打包的时候会把项目里面的所有jar文件进行一次合并、压缩、重新编译变成classes.dex文件被放在APK根目录下。当应用被执行的时候Android系统内的Java虚拟机(Dalvik或者Art),会去解读classes.dex里面的字节码并且执行。把众多jar包编译成classes.dex文件是打包Android应用不可或缺的一步。
看到这里有人可能会想不对啊,这一步只将jar包打成dex文件,那之前的java文件生成jar文件难道不是在这一步做吗?没错,这里用的jar包一般是由其他Android的IDE生成完成后再拷贝过来的。本文后面的部分会涉及到怎么使用Android的IDE并且生成必要的文件。
● libs文件夹的*.so文件则是可以动态的被Android系统加载的库文件,一般是由C/C++撰写而成然后编译成的二进制文件。要注意的是,由于实际执行这些二进制库的CPU的架构不一样,所以同样的CC++代码一般会针对不同的CPU架构生成几分不同的文件。这就是为什么libs文件夹里面通常都有armeabi-v7a、armeabi、x86等几个固定的文件夹,而且里面的.so文件也都是有相同的命名方式。Java虚拟机在加载这些动态库的时候会根据当前CPU的架构来选择对应的so文件。有时候这些so文件是可以在不同的CPU架构上执行的,只是在不对应的架构上执行速度会慢一些,所以当追求速度的时候可以给针对每个架构输出对应的so文件,当追求包体大小的时候输出一个armeabi的so文件就可以了。
● assets文件夹,这个里面的东西最简单了,在打包APK的时候,这些文件里面的内容会被原封不动的被拷贝到APK根目录下的assets文件夹。这个文件夹有几个特性。
√ 里面的文件基本不会被Android的打包工具修改,应用里面要用的时候可以读出来。
√ 打出包以后,这个文件夹是只读的,不能修改。
√ 读取这个文件夹里面的内容的时候要通过特定的Android API来读取,参考getAssets()。
√ 基于上述两点,在Unity中,要读取这部分内容要通过WWW来进行加载。
除了Plugins/Android内的所有assets文件夹里面的文件会连同StreamingAssets目录下的文件一起被放到APK根目录下的assets文件夹。
● res文件夹里面一般放的是xml文件以及一些图片素材文件。xml文件一般来说有以下几种:
√ 布局文件,被放在res中以layout开头的文件夹中,文件里描述的一般都是原生界面的布局信息。由于Unity游戏的显示是直接通过GL指令来完成的,所以我们一般不会涉及到这些文件。
√ 字符串定义文件,一般被放到values文件夹下,这个里面可以定义一些字符串在里面,方便程序做国际
化还有本地化用。当然有时候被放到里面的还有其他xml会引用到的字符串,一般常见的是app的名称。
√ 动画文件,一般定义的是Android原生界面元素的动画,对于Unity游戏,我们一般也不会涉及他。
√ 图片资源,一般放在以drawable为开头的文件夹内。这些文件夹的后缀一般会根据手机的像素密度来来进行区分,这样我们可以往这些文件夹内放入对应像素密度的图片资源。
例如后缀为ldpi的drawable文件夹里面的图片的尺寸一般来说会是整个系列里面最小的,因为这个文件夹的内容会被放到像素密度最低的那些手机上运行。而一般1080p或者2k甚至4k的手机在读取图片的时候会从后缀为xxxxhdpi的文件夹里面去读,这样才可以保证应用内的图像清晰。图片资源在打包过程中会被放到APK的res文件夹内的对应目录。
√ Android还有其他一些常见的xml文件,这里就不一一列举了。
res文件夹下的xml文件在被打包的时候会被转换成一种读取效率更高的一种特殊格式(也是二进制的格式),命名的时候还是以xml为结尾被放到APK包里面的res文件夹下,其目录结构会跟打包之前的目录结构相对应。
除了转换xml之外,Android的打包工具还会把res文件夹下的资源文件跟代码静态引用到的资源文件的映射给建立起来,放到APK根目录的resources.arsc文件。这一步可以确保安卓应用启动的时候可以加载出正确的界面,是打包Android应用不可或缺的一步。
● AndroidManifest.xml,这份文件太重要了,这是一份给Android系统读取的指引,在Android系统安装、启动应用的时候,他会首先来读取这个文件的内容,分析出这个应用分别使用了那些基本的元素,以及应该从classes.dex文件内读取哪一段代码来使用又或者是应该往桌面上放哪个图标,这个应用能不能被拿来debug等等。在后面的部分会有详细解释。打包工具在处理Unity项目里面的AndroidManifest文件时会将所有AndroidManifest文件的内容合并到一起,也就是说主项目引用到的库项目里面如果也有AndroidManifest文
件,都会被合并到一起。这样就不需要手动复制粘贴。需要说明的是,这份文件在打包Android程序的时候是必不可少的,但是在Unity打包的时候,他会先检查Plugins目录下有没有这份文件,如果没有就会用一个自带的AndroidManifest来代替。此外,Unity还会自动检查项目中AndroidManifest里面的某些信息是不是默认值,如果是的话,会拿Unity项目中的值来进行替换。例如,游戏的App名称以及图标等。
● project.properties,这份文件一般只有在库项目里面能看得到,里面的内容极少,就只有一句话android.library=true。但是少了这份文件Android的打包工具就不会认为这个文件夹里面是个Android的库项目,从而在打包的时候整个文件夹会被忽略。这有时候不会影响到打包的流程,打包过程中也不会报错,但是打出的APK包缺少资源或者代码,一跑就崩溃。关于这份文件,其实在Unity的官方文档上并没有详细的描述(因为他实际上是Android项目的基础知识),导致很多刚刚接触Unity-Android开发的开发者在这里栽坑。曾
经有个很早就开始用Unity做Android游戏的老前辈告诉我要搞定Unity中的Android库依赖的做法是用Eclipse打开Plugins/Android文件夹,把里面的所有的项目依赖处理好就行了。殊不知这样将Unity项目跟Eclipse项目耦合在一起的做法是不太合理的,会造成Unity项目开启的时候缓慢。
● 其他文件夹例如aidl以及jni在Unity生成APK这一步一般不会涉及到,这里不展开。
看到了上述介绍的Unity打包APK的基础知识我们知道了往Plugins/Android目录下放什么样的文件会对APK包产生什么样的影响。但是实际上上述的内容只是着重的讲了Unity是怎么打包APK,所以接下来会简述一下打包这个步骤到底是怎么完成的。
Android提供了一个叫做aapt的工具,这个工具的全称是Android Asset Packaging Tool,这个工具完成了上述大部分的对资源文件处理的工作,而Unity则是通过对Android提供的工具链(Android Build Tools)的一系列调用从而完成打包APK的操作。这里感觉有点像我们写了个bat/bash脚本,这个脚本按照顺序调用Android提供的工具一样。在一些常见的Android IDE里面,这样的“bat/bash脚本”往往是一个完整的构建系统。最早的Android IDE是Eclipse,他的构建系统是Ant,是基于XML配置的构建系统。后来Android团队推出了Android专用的IDE——Android Studio(这个在文章后面会有详述),他的构建系统则是换成了gradle,从基于xml的配置一下子升级到了语言(DSL, Domain Specific Language)的层级,给使用Android Studio的人带来更多的弹性。
写到这里我想很多人都清楚了要怎么把Android的SDK/插件放到Unity里面并且打包到Unity里面。这时候应该有人会说,光会放这些文件不够啊,我还需要知道自己怎么写Android的代码并且输出相应的SDK/插件给Unity使用啊。
更多unity2018的功能介绍请到paws3d爪爪学院查找。