一、前言
嗨,大家好,我是新发。 最近在忙的谷歌提审的事情,涉及到Unity的Gradle打包,感觉有必要写一篇文章讲讲,话不多说,我们开始吧。
特别说明,我的电脑环境如下: 外网机:win10操作系统,Unity版本2019.4.17f1c1; 内网机:win7操作系统,Unity版本2017.4.29f1。
二、Gradle与Android Gradle Plugins
关于Gradle与Android Gradle Plugins,相信刚使用Gradle的小伙伴会傻傻分不清,不要怕,今天我就来给你讲清楚。 
1、Gradle工具
1.1、Gradle工具简介
Gradle是一个项目构建工具,它主要是为了解决项目的自动化构建。Gradle同时也是一个开发框架,它有自己的开发语言Groovy, 我们可以通过Groovy语言去写自己的Gradle插件,也可以去编写指定的脚本去改变构建规则。 
这样讲或许对于萌新小伙伴还是不大好理解,为什么要自动化构建,我以一名Unity游戏开发工程师的角度来解释一下,实际项目我们可能会打包上架到各大应用市场,比如应用宝、华为、小米、OPPO、VIVO、Google等等,一个工程要打那么多渠道包,每个渠道包集成的SDK不同,还有一些配置、资源差异等等,我们不可能挨个手动修改然后再打包。 还好Unity提供了各种Editor的API方便开发者自定义打包工具,另外我们还可以通过命令行调用我们自己实现的打包工具,如果再集成jenkins,那么打包各个渠道包就完全可以交给策划的同事来执行了,支持远程自动化批量打包,实在不能太香了。
注:关于打多渠道包这样的需求,市面上也有一些聚合SDK帮开发者做了工具,不过主动权就不在自己这边了,这个具体场景具体分析,实际应用场景可能更加复杂,需求多变,我们可能依然还是需要自己实现打包工具。

同样的道理,Gradle就是为了实现类似上面我讲的这个需求而生的。 Gradle被设计成支持跨多种语言和平台的构建自动化,包括Java、Scala、Android、C/C++和Groovy,所以它并不是Android的专属哦。 如果你访问Gradle的GitHub:https://github.com/gradle/gradle 你就可以看到它可以构建各种类型的项目, 
我们看Gradle的LOGO是一只三条腿的大象,少了一条腿,或许是想告诉开发者:你可以自己定义第四条腿,自由发挥,一方面展现了Gradle的功能的强大,另一方面又表达了它良好的可扩展性和包容性。(纯属个人YY) 
1.2、Gradle工具下载
Gradle工具各个发行版本可以从这里下载:https://services.gradle.org/distributions/  从上面我们可以看到Gradle的迭代更新是非常快的,它是开源的,社区非常活跃。 这是Gradle的Github地址:https://github.com/gradle/gradle 感兴趣的同学可以下载下来学习它的源码。 
1.3、在Android Studio中配置Gradle工具
如果你用过Android Studio,那么你肯定对Gradle不陌生,我们在gradle-wrapper.properties中配置的distributionUrl就是Gradle工具的下载路径,   我们也可以先把Gradle的某个版本下载到本地,然后把distributionUrl改成本地路径,例:  画成图是这样子:  需要注意,上面我说的的gradle-wrapper.properties其实是Gradle Wrapper的配置。 嗯?Gradle Wrapper是啥?与Gradle又是什么关系? Gradle Wrapper是Gradle的又一层封装,正如上面你看到Gradle有很多个版本,不同项目可能使用不同的Gradle版本来构建,所以就又封装了个Gradle Wrapper,方便不同项目配置不同版本的Gradle,嘛,再画个图吧~  我们在Android项目的gradle/wrapper目录中可以看到gradle-wrapper.jar,它就是Gradle Wrapper本君啦~  Gradle Wrapper干的主要事情就是下载安装Gradle,你可以用jd-gui反编译一下gradle-wrapper.jar,如下:
注:jd-gui是java的反编译工具,直接把.jar文件拖到jd-gui中即可看到java代码了。 jd-gui下载地址:https://jd-gui.apponic.com/

1.4、在Unity中配置Gradle工具
现在,我们对应回Unity,我以2019.4.17f1c1版本为例,点击菜单Edit / Preferences...,  打开Preferences窗口,点击External Tools按钮,即可看到Gradle的路径配置,  这与上面我们讲的在Android Studio的gradle-wrapper.properties中配置本地Gradle是一样的道理。 其实Unity自身集成一个Gradle工具,在Unity的安装目录中, 路径:Unity安装目录\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradle\lib 我们进到该目录就可以看到Gradle工具本君啦,  我们也可以自行去这里 https://services.gradle.org/distributions/ 下载其他版本的Gradle,然后在Unity的Preferences窗口中设置为本地的其他版本的Gradle的路径。 为了方便大家理解,我再画个图,  另外,如果你的Unity版本是2018或以下版本,在Preferences窗口中就没有Gradle路径的设置了,我以Unity2017为例,如下  但不代表Unity没有内置Gradle,早在Unity5.x版本就已经内置了Gradle了,我们可以进到这个目录:Unity安装目录\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\gradle\lib,依然可以看到Unity的Gradle哦,只是不同版本的Unity内置的Gradle版本不同, 
1.5、在Unity中开启Gradle方式构建项目
Unity2018或以下版本,我们在Build Settings中可以设置Build System为Internal或Gradle,以以Unity2017为例,如下  到了Unity2019就默认只能以Gradle方式构建项目了, 
2、Android Gradle Plugins插件
2.1、Android Gradle Plugins与Gradle的关系
Gradle工具具有强大的可扩展性,我们可以为其写插件来实现特定类型的项目的自动化构建。 为了让Gradle能够更加便捷地构建Android项目,Google团队开发了Android Gradle Plugins插件,注意,Android Gradle Plugins是Google开发的,不是Gradle团队开发的,不要搞混啦,画个图方便大家记住~  关于Android Gradle Plugins的更多介绍,可以访问官方文档: https://developer.android.google.cn/studio/releases/gradle-plugin

2.2、Android Gradle Plugins手动下载
Android Gradle Plugins在哪里下载呢? 一般是从Google的maven仓库下载,我们可以在浏览器访问Google的maven网站:https://mvnrepository.com/  搜索android gradle plugins即可找到啦,  点进去可以看到Android Gradle Plugins也有很多个版本, 
2.3、Android Gradle Plugins版本与Gradle版本的对应关系
Gradle有好多个版本,Android Gradle Plugins也有好多个版本,特定版本的Android Gradle Plugins需要对应特定版本的Gradle,否则它们之间可能不能正常协同工作,毕竟,它们是两个开发团队开发的,Gradle在快速迭代,Android Gradle Plugins也在快速迭代,为此,Google专门列出了Android Gradle Plugins与Gradle的版本对应关系表格:
具体可以参见Google的《Android Gradle 插件版本说明》
| Android Gradle Plugins插件版本 | 所需的 Gradle 版本 |
|---|
| 1.0.0 - 1.1.3 | 2.2.1 - 2.3 | | 1.2.0 - 1.3.1 | 2.2.1 - 2.9 | | 1.5.0 | 2.2.1 - 2.13 | | 2.0.0 - 2.1.2 | 2.10 - 2.13 | | 2.1.3 - 2.2.3 | 2.14.1 - 3.5 | | 2.3.0+ | 3.3+ | | 3.0.0+ | 4.1+ | | 3.1.0+ | 4.4+ | | 3.2.0 - 3.2.1 | 4.6+ | | 3.3.0 - 3.3.3 | 4.10.1+ | | 3.4.0 - 3.4.3 | 5.1.1+ | | 3.5.0 - 3.5.4 | 5.4.1+ | | 3.6.0 - 3.6.4 | 5.6.4+ | | 4.0.0+ | 6.1.1+ | | 4.1.0+ | 6.5+ | | 4.2.0+ | 6.7.1+ |
2.4、Android Studio中配置Android Gradle Plugins
上面我们是手动去Google的maven仓库下载Android Gradle Plugins,这种需要手动下载的操作,在Gradle面前,是不存在滴。 我们以Android Studio为例,打开Project的build.gradle文件,  我们只需要在dependencies中配置一下com.android.tools.build:gradle:版本即可,如下  细心的同学应该注意到了repositories中默认是google()和jcenter(),即优先从Google的Maven仓库下载依赖库,  当然,我们可以在前面添加国内的源来提升下载速度,比如改为阿里云的源:
repositories {
maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
maven{ url 'http://maven.aliyun.com/nexus/content/repositories/jcenter'}
}
如下: 
2.5、Unity中配置Android Gradle Plugins
对应回Unity项目,我以Unity2017.4.29f1版本为例,在Player Settings中,我们点开Publishing Settings,在里面可以看到有个Custom Gradle Template勾选,我们把它勾选上,  此时会在Assets/Plugins/Android目录中生成一个mainTemplate.gradle文件,  它就等价于上面Android Studio中的build.gradle文件,我们打开mainTemplate.gradle文件,可以看到它依赖了3.2.0版本的Android Gradle Plugins,我们可以修改为其他版本,但一般默认即可;另,它也是默认从google和jcenter仓库下载依赖库的,
// GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.2.0'
}
}
allprojects {
repositories {
google()
jcenter()
flatDir {
dirs 'libs'
}
}
}
apply plugin: 'com.android.application'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
**DEPS**}
android {
compileSdkVersion **APIVERSION**
buildToolsVersion '**BUILDTOOLS**'
defaultConfig {
minSdkVersion **MINSDKVERSION**
targetSdkVersion **TARGETSDKVERSION**
applicationId '**APPLICATIONID**'
ndk {
abiFilters **ABIFILTERS**
}
versionCode **VERSIONCODE**
versionName '**VERSIONNAME**'
}
lintOptions {
abortOnError false
}
aaptOptions {
noCompress '.unity3d', '.ress', '.resource', '.obb'**STREAMING_ASSETS**
}
**SIGN**
buildTypes {
debug {
minifyEnabled **MINIFY_DEBUG**
useProguard **PROGUARD_DEBUG**
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-unity.txt'**USER_PROGUARD**
jniDebuggable true
}
release {
minifyEnabled **MINIFY_RELEASE**
useProguard **PROGUARD_RELEASE**
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-unity.txt'**USER_PROGUARD**
**SIGNCONFIG**
}
}
**PACKAGING_OPTIONS**
bundle {
language {
enableSplit = false
}
density {
enableSplit = false
}
abi {
enableSplit = true
}
}
}
**SOURCE_BUILD_SETUP**
上面的mainTemplate.gradle文件其实是从Unity的.gradle模板文件拷贝过来的。 我们可以在Unity安装路径/Editor/Data/PlaybackEngines/AndroidPlayer/Tools/GradleTemplates目录中看到三个.gradle模板文件,如下,我们可以修改这些.gradle模板文件,比如修改它的源改为阿里云之类的。  如果你是Unity2019,比如以2019.4.17f1c1为例,则会看到Publishing Settings中变成了三个勾选,  勾选后在Assets/Plugins/Android目录中可以看到对应的gradle文件,  同样,我们在Unity安装路径/Editor/Data/PlaybackEngines/AndroidPlayer/Tools/GradleTemplates目录中可以看到.gradle模板文件,对比Unity2017是略有差异的,  Android Gradle Plugins的依赖是在baseProjectTemplate.gradle中配置的,如下,可以看到2019.4.17f1c1依赖的Android Gradle Plugins版本为3.6.0,
// GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN
allprojects {
buildscript {
repositories {**ARTIFACTORYREPOSITORY**
google()
jcenter()
}
dependencies {
// If you are changing the Android Gradle Plugin version, make sure it is compatible with the Gradle version preinstalled with Unity
// See which Gradle version is preinstalled with Unity here https://docs.unity3d.com/Manual/android-gradle-overview.html
// See official Gradle and Android Gradle Plugin compatibility table here https://developer.android.com/studio/releases/gradle-plugin
// To specify a custom Gradle version in Unity, go do "Preferences > External Tools", uncheck "Gradle Installed with Unity (recommended)" and specify a path to a custom Gradle version
classpath 'com.android.tools.build:gradle:3.6.0'
**BUILD_SCRIPT_DEPS**
}
}
repositories {**ARTIFACTORYREPOSITORY**
google()
jcenter()
flatDir {
dirs "${project(':unityLibrary').projectDir}/libs"
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
三、Unity离线环境下使用Gradle打包的问题
一般实际项目都是在内网开发的,即在离线环境下开发,无法连接网络。而我们上面说到,使用Gradle构建Android项目时,它是依赖Android Gradle Plugins插件的,默认情况下会去google和jcenter仓库下载依赖的库,  显然,此时你在离线环境打包就会报这样的错:  事实上,Unity会先帮我们把Unity工程转成Gradle工程,我们可以在工程路径下的Temp/gradleOut中看到生成的Gradle工程,看到那个build.gradle文件了吗,它就是我们上文说的.gradle模板文件生成出来的,  离线环境无法连接google和jcenter的maven仓库,那我们就自己搭建一个离线版的maven仓库吧,画图, 
四、搭建离线maven仓库
1、Google官方离线版maven仓库
Google提供了一个离线版的maven仓库,下载地址:https://developer.android.com/studio#downloads  我们可以看到这个离线版的maven仓库有2.7G那么大,如果我们只是需要解决Android Gradle Plugins依赖的问题,我们的离线仓库其实只需要Android Gradle Plugins和它依赖的库即可,没必要下载Google这个2.7G的仓库,下面我就来教大家制作一个迷你版的离线仓库。
2、自制迷你版maven仓库
原理:本地仓库是远程仓库的一个缓冲和子集,首先会从本地仓库查找资源,如果没有,则从远程仓库下载到本地仓库,我们就可以利用这个缓冲子集来制作迷你版离线仓库啦。
撸起袖子开干~
Gradle的maven缓冲路径为C:\Users\当前用户\.gradle\caches,我们在外网环境下(即可以连接网络的电脑上),先看下本地这个路径C:\Users\当前用户\.gradle\caches,如果caches目录中有文件,先清空它,如果找不到这个cache目录,则直接下一步,  使用Unity创建一个空工程,使用Gradle方式构建Android项目,此时Gradle会下载依赖的库到这个caches目录中,因为我们清空了caches,所以这次打包时间会比较长,耐心等待。 打包完毕后我们就可以看到这个caches目录中多了一些文件出来啦,这些就是下载下来的缓存文件,  整个caches文件夹只需要132M,  我们把整个.caches文件夹压缩后传到内网,用于制作离线版maven仓库。  如下,转到了内网机中,  解压到当前文件夹,  解压后进入caches/modules-2/files-2.1目录中,可以看到各个依赖库的文件夹,  如果你对比过真实的maven仓库文件夹你就会发现,上面这种文件结构是不对的,举个例子,比如com.android.tools.build,  正确的maven仓库路径会拆分成多级目录,即:com/android/tools/build,我们要以.为分割拆分成多级目录。 另外,我们继续进到目录里面,以gradle-3.2.0.jar为例,可以看到它被放在com.android.tools.build\gradle\3.2.0\1851dd6a2badb1a66e5fcafc311073d7ad0b3183这个目录中,  比正确的maven仓库多了一层1851dd6a2badb1a66e5fcafc311073d7ad0b3183目录,我们需要把gradle-3.2.0.jar放到上层目录中,即最终正确的路径应该为: com\android\tools\build\gradle\3.2.0\gradle-3.2.0.jar, 同理,其他文件也是按照这个路径规则调整,嘛,写个python脚本批量处理一下吧~ 在caches同级目录中创建一个gen_repository.py脚本,  gen_repository.py脚本代码如下:
import os
import shutil
def walk_caches(caches_path):
for root, dirs, fs in os.walk(caches_path):
for f in fs:
yield (root, f)
if '__main__' == __name__:
caches_path = 'caches/modules-2/files-2.1/'
for (root, f) in walk_caches(caches_path):
dir_tmp = root.replace(caches_path, '')
module_name = dir_tmp[0:dir_tmp.find('\\')]
module_split_path = module_name.replace('.', '/')
target_dir = './repository/' + module_split_path + dir_tmp[dir_tmp.find('\\'):dir_tmp.rfind('\\')]
if not os.path.exists(target_dir):
os.makedirs(target_dir)
target_file_path = os.path.join(target_dir, f)
if not os.path.exists(target_file_path):
shutil.copy(os.path.join(root, f), target_file_path)
print('copy ' + f)
print('done')
执行一下gen_repository.py脚本,生成了一个repository,我们的离线版迷你仓库就这样制作完成啦,记住它的路径:F:/GoogleMaven/repository,下文需要用到这个路径。 
3、配置本地离线版maven
用过maven的同学应该知道,离线maven仓库默认路径是在C:\Users\当前用户\.m2\repository,嘛,C盘空间寸土寸金,肯定不要放C盘里。还好maven还提供了一个settings.xml给我们,我们只需要在C:\Users\当前用户\.m2目录中放一个settings.xml并配置具体的repository路径即可,如果你本机没有C:\Users\当前用户\.m2这个目录则手动创建一个,  settings.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>F:/GoogleMaven/repository</localRepository>
<pluginGroups>
<pluginGroup>org.sonatype.plugins</pluginGroup>
<pluginGroup>org.mortbay.jetty</pluginGroup>
</pluginGroups>
<proxies>
</proxies>
<servers>
</servers>
<mirrors>
</mirrors>
<profiles>
</profiles>
<activeProfiles>
</activeProfiles>
</settings>
最重要的就是这句:
<localRepository>F:/GoogleMaven/repository</localRepository>
接着,修改一下Unity安装路径下的的.gradle模板文件的仓库为本地仓库(mavenLocal),  如下:   在Unity中勾选Custom Gradle Template,  此时就会从Unity安装路径下拷贝.gradle模板文件到工程路径中Assets/Plugins/Android,  如果你之前拷贝了旧的gradle模板文件,则记得把工程中的.gradle文件的仓库也改成本地仓库(mavenLocal),如下  画个图方便大家理解, 
4、Unity使用Gradle打包测试
在Build Settings中设置Build System为Gradle,
注:如果你是Unity2019或以上版本,则默认是使用Gradle来构建Android,不需要设置Build System。
 执行Build打包,打包成功,  成功生成apk,如下 
五、完毕
好了,就先写这么多吧,另外再啰嗦一下,今年很多项目会选择出海,上架Google需要使用AAB格式,必须使用Gradle方式打包,同时必须支持x64架构,所以必须使用IL2CPP编译才能发布x64架构的应用,预祝大家出海成功,我是新发,喜欢我的文章的可以点点关注,有任何疑问的可以留言或私信,能帮上的我都会尽量解答,好了,肚子饿了,我要去吃午饭了,拜拜~
|