一、前言
嗨,大家好,我是新发。 最近在忙的谷歌提审的事情,涉及到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 架构的应用,预祝大家出海成功,我是新发,喜欢我的文章的可以点点关注,有任何疑问的可以留言或私信,能帮上的我都会尽量解答,好了,肚子饿了,我要去吃午饭了,拜拜~
|