Unity基于Gradle的Android打包

Unity打包apk一般有两种方式,一种是直接在Unity导出apk,还有一种是先导出gradle工程,再通过外部工具对gradle工程进行构建(如Android Studio).

一、什么是Gradle

Gradle是一个项目自动化构建的开源工具,基于JVM,它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置.DSL语法和Java相接近.

二、Unity导出Gradle工程

  • 使用UnityHub安装依赖项: 下载"Android Build Support",并且勾选AndroidSDK和NDK工具,以及OpenJDK.

    Unity将AndroidSDK&NDK和OpenJDK分别安装在/[EditorVersion]/Editor/Data/PlaybackEngines/AndroidPlayer/SDKNDKOpenJDK中

    image-20240513230502949

  • 每个Unity版本支持的每个依赖项的版本信息都是不同的,每个Unity都需要特定版本的Android NDK和Android JDK,但Android SDK没有确切的版本要求

    image-20240513230912531

    image-20240513231015844

    image-20240513231027127

  • 适用于Android的外部工具: External Tools:

    image-20240513231349815

  • 打包时勾选Export Project点击Export后就会得到Gradle文件夹.

三、Unity的Grade的层次结构

image-20240513232419374

3.1 Launcher

image-20240513232734066

启动器部分,其中包含应用程序的名称及其图标.默认是可启动Unity的简单Android应用程序.

  • src:启动器模块的代码和资源
  • build.gradle:
    • 描述如何构建启动器模块,并包含要在构建中包含的依赖项列表
    • 依赖于unityLibrary模块,当构建启动器模块时,unityLibrary必须包含在最终结果中
    • 对呀自定义Gradle的Custom Launcher Gradle Template文件

3.2 UnityLibrary

image-20240513234140800

Unity模块,包含Unity运行时和播放器数据,可以集成到其他任何Gradle项目中.

  • libs:

    • 用于存放unityLibrary模块的Android Archive(.arr)和java Archive(.jar)插件
    • 导出的Unity项目,包含了unity-classes.jar文件(Unity引擎使用的java代码)
  • src:包含unityLibrary模块的源代码和资源.Unity将源代码和资源放在次目录中(AndroidManifest.xml和java代码等).

  • build.gradle:

    • 描述了如何构建unityLibrary模块,并包含了构建中要包含的依赖项列表
    • unityLibrary模块依赖于Unity项目中的所有插件
    • 对呀自定义Gradle的Custom Main Gradle Template文件

3.3 build.gradle

  • 整体项目的构建逻辑,负责引入所需的全部子项目Gradle并触发每个构建命令
  • 文件位于项目的根目录下,用于定义适用于项目中的所有模块的依赖项.
  • 会影响所有模块的配置.
  • 可包含用于清理build目录的代码
  • 用于指定Android Gradle Plugin版本
  • 对呀自定义Gradle的 Custom Base Gradle Template文件

3.4 gradle.properties

  • 配置了Gradle和JVM的属性
  • 配置守护进程并管理构建过程中启动JVM的方式
  • Unity默认添加了数据流文件资源目录中的资源类型unityStreamingAssets=.unity3d,告诉Gradle不应该压缩它们。多个文件用","进行分割。
  • 对呀自定义Gradle的Custom Gradle Properties Template文件

3.5 local.properties

  • 用于配置本地环境属性,如Android SDK或者Android NDK

3.6 setting.gradle

  • 用于多项目构建或者具有子项目的项目的定义文件
  • Unity中通常包含launcher和unityLibrary
  • 如果使用Play Asset Delivery,由于所有的资源包都是单独的模块,所以将会列出每个资源模块.

四、自定义Gradle模块

4.1 自定义Gradle模板变量

变量 描述
dependencies 项目依赖项(即,项目使用的库)的列表。
compileSdkVersion 构建针对的 API 版本(例如 25)
buildToolsVersion 使用的 SDK 构建工具(例如 25.0.1)
minSdkVersion 最小兼容的Android平台版本
targetSdkVersion 目标 API 版本(例如,25)
versionCode 版本码,递增的整数
versionName 版本名
consumerProguardFiles Proguard的配置文件
applicationId Android 应用程序 ID(例如,com.mycompany.myapp)

4.2 Custom Main Gradle Template

此文件包含有关如何将Unity的 Android 应用程序构建为库的配置信息,对应Gradle结构的"unityLibrary/build.gradle"文件.

//表述这个工程的工程类型,一般包含3种:
//App类型工程:apply plugin: 'com.android.application'
//库类型工程:apply plugin: 'com.android.library'
//Test工程类型:apply plugin: 'com.android.test'
apply plugin: 'com.android.library'

//指构建工程所依赖的所有依赖项
dependencies {
    //一般来说有3种依赖类型
    //对于本地模块的依赖:implementation project(':mylibrary') [mylibrary此名称必须与在您的 settings.gradle 文件中使用 include: 定义的库名称相符)]
    //本地文件依赖项:implementation fileTree(dir: 'libs', include: ['*.jar'])[对项目的libs目录中 JAR 文件的依赖关系,Gradle会读取build.gradle文件的相对路径]
    //也可以按如下方式指定各个文件:implementation files('libs/foo.jar', 'libs/bar.jar')
    //远程文件依赖项:implementation 'com.example.android:app-magic:12.3'[实际是implementation group: 'com.example.android', name: 'app-magic', version: '12.3'的简写]
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}
//针对Android选项的配置块
android {
    //编译依赖的SDK版本
    compileSdkVersion 29
    //构建工具版本
    buildToolsVersion '30.0.2'

    //编译选项
    compileOptions {
        //这里配置的是Java 语言的的源码版本
        sourceCompatibility JavaVersion.VERSION_1_8
        //这里配置的是Java生成的字节码版本
        targetCompatibility JavaVersion.VERSION_1_8
    }
    //默认的配置
    defaultConfig {
        //最小兼容的Android平台版本,如果低于此版本将会阻止用户安装。
        minSdkVersion 19
        //指定我们基于哪个版本开发。
        targetSdkVersion 29

        ndk {
            //ABI 是 Application Binary Interface 的缩写。帮助适配不同的CPU架构。
            //把除了v7a以外的兼容包都过滤掉
            abiFilters 'armeabi-v7a'
        }
        //版本码,是一个递增的整数
        versionCode 1
        //版本名
        versionName '1.0.2'
        //Proguard的配置文件,这里面有两个文件一个是unity自身的配置还有一个是我们自己配置的Proguard配置文件
        consumerProguardFiles 'proguard-unity.txt', 'proguard-user.txt'
    }
    //Lint选项配置
    lintOptions {
        //当发生错误不终止构建
        //更多lint配置:https://developer.android.google.cn/reference/tools/gradle-api/4.1/com/android/build/api/dsl/LintOptions?hl=cn
        abortOnError false
    }
    //aapt全称为Android Asset Packaging Tool,为Android资源打包工具。
    //https://developer.android.google.cn/reference/tools/gradle-api/4.1/com/android/build/api/dsl/AaptOptions?hl=en
    aaptOptions {
        //这里表示这些文件类型不会被压缩存储在APK中。
        //unityStreamingAssets.tokenize 这里获取的是gradle.properties文件中unityStreamingAssets的配置。
        noCompress = ['.ress', '.resource', '.obb'] + unityStreamingAssets.tokenize(', ')
        //需要忽略的资源类型
        ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
    }
    //Packaging选项
    //https://developer.android.google.cn/reference/tools/gradle-api/7.4/com/android/build/api/dsl/PackagingOptions?hl=en
    packagingOptions {
        //设置armeabi-v7a相关文件不会被剥离优化压缩
        doNotStrip '*/armeabi-v7a/*.so'
    }
}

4.3 Custom Launcher Gradle Template

此文件包含有关如何构建 Android 应用主模块的配置,对应Gradle结构的"unityLibrary/launcher.gradle"文件。

//此模块为App应用
apply plugin: 'com.android.application'

dependencies {
    //依赖于unityLibrary本地模块
    implementation project(':unityLibrary')
}

android {
    compileSdkVersion 29
    buildToolsVersion '30.0.2'

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    defaultConfig {
        minSdkVersion 19
        targetSdkVersion 29
        //设置applicationId,appi要与包名一致,这里Unity会帮我们通过playersetting设置
        applicationId 'com.DefaultCompany.com.unity.template.mobile2D'
        ndk {
            abiFilters 'armeabi-v7a'
        }
        versionCode 1
        versionName '1.0.2'
    }

    aaptOptions {
        noCompress = ['.ress', '.resource', '.obb'] + unityStreamingAssets.tokenize(', ')
        ignoreAssetsPattern = "!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~"
    }

    lintOptions {
        abortOnError false
    }
    //这里用于配置我们不同的构建类型
    buildTypes {
        debug {
            //是否启用混淆,启用之后增加反编译成本
            minifyEnabled false
            //设置混淆文件规则
            //getDefaultProguardFile('proguard-android.txt')从Android SDK tools/proguard/文件夹获取默认的 ProGuard规则文件,为我们提供的默认混淆规则文件。
            //proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro' 可以设置自定义混淆规则文件。
            //proguard-rules.pro用于添加自定义位于模块根目录。
            proguardFiles getDefaultProguardFile('proguard-android.txt')
            //签名配置
            signingConfig signingConfigs.debug
            //是否激活NDK调试
            jniDebuggable true
        }
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt')
            signingConfig signingConfigs.debug
        }
    }

    packagingOptions {
        doNotStrip '*/armeabi-v7a/*.so'
    }
    //改进的应用程序打包方式,能大幅度减少应用体积。
    //默认情况下,构建器会自动根据CPU架构对屏幕分辨率、语言等维度将app 分拆,如果希望自由控制分拆维度,可以在此控制。
    //android 5.0以下不支持bundle
    bundle {
        //对语言进行拆分
        language {
            enableSplit = false
        }
        //对屏幕密度进行拆分
        density {
            enableSplit = false
        }
        //对不同CPU架构进行拆分
        abi {
            enableSplit = true
        }
    }
}

4.4 Custom Base Gradle Template

//在这里指定所有模块的依赖项和远程仓
//当只有单个模块需要的依赖项应该放在对应的模块级的build.gradle文件中。
allprojects {
    //gradle脚本执行所需的依赖项和远程仓
    buildscript {
        //使用JCenter和Google作为远程仓,本质就是个Maven仓库
        repositories {
            google()
            jcenter()
        }
        //配置依赖关系
        dependencies {
            classpath 'com.android.tools.build:gradle:3.6.0'            
        }
    }
    //当前项目所需的依赖项和远程仓
    //使用JCenter和Google作为远程仓
    repositories {
        google()
        jcenter()
        //设置源类型目录位置
        //这里设置我们的unityLibrary模块的libs文件夹
        flatDir {
            dirs "${project(':unityLibrary').projectDir}/libs"
        }
    }
}
//定义了一个任务,用于删除项目中build文件夹下的内容
task clean(type: Delete) {
    delete rootProject.buildDir
}

4.5 Custom Gradle Properties Template

此文件包含 Gradle 构建环境的配置设置,对应Gradle结构的"gradle.properties"文件。

//分配给守护进程,JVM内存的大小,分配较高的值对提高构建性能特别有用。
org.gradle.jvmargs=-Xmx4096M
//多个构建任务进行并行执行
org.gradle.parallel=true
//Android Gradle插件3.4.0或更高版本时可用,R8和Proguard 相比,R8 可以更快地缩减代码,同时改善输出大小。
android.enableR8=false
//不对.unity3d在构建应用时进行压缩
unityStreamingAssets=.unity3d

五、 Gradle构建APK

利用python脚本在Gradle工程中构建apk

  • 构建apk脚本:
import os
# gradle路径 user\.gradle\wrapper\dists\gradle版本号
Gradle =r"C:\Users\lwy\.gradle\wrapper\dists\gradle-6.1.1-bin\4i3ntwqiazourd86epxcz427c\gradle-6.1.1\bin"

def StartBuild(gradlePath):
    os.chdir(gradlePath)
    # jdk路径
    os.putenv("JAVA_HOME", r"C:\Users\lwy\.jdks\corretto-1.8.0_412")
    # android sdk路径
    os.putenv("ANDROID_HOME", r"D:\Android\SDK")
    # gradle路径
    os.putenv("GRADLE_BIN", r"C:\Users\lwy\.gradle\wrapper\dists\gradle-6.1.1-bin\4i3ntwqiazourd86epxcz427c\gradle-6.1.1\bin")

    os.system('{}\gradle clean'.format(Gradle))
    os.system('{}\gradle init'.format(Gradle))
    os.system('{}\gradle wrapper'.format(Gradle))

    os.system('gradlew assembleDebug')

if __name__ == '__main__':
    # 游戏工程路径
    gradlePath = r"D:\Test\Fina"
    StartBuild(gradlePath)
    os.system("pause")
  • 内网制作android gradle plugin库
  1. 复制android gradle plugin本地仓库(需在外网下载)C:\Users\lwy.gradle\caches\modules-2\files-2.1

  2. 利用(ProcessAndroidDependenceFile.py)脚本将文件夹变为可识别仓库

  3. 修改build.gradle的maven 路径(整个文件)

    ProcessAndroidDependenceFile.py

     import os
     import shutil
     import sys
     # 本地缓存路径
     srcFolderPath = "C:\Users\lwy\.gradle\caches\modules-2\files-2.1"
     # 目标路径
     dstFolderPath = "D:/Android/repository"
    
     class DataInfo:
         def __init__(self, destPath, srcPath):
             self.DestPath = destPath
             self.SrcPath = srcPath
    
     def Start():
         print("ProcessStart=>" + srcFolderPath)
         FilesList = []
    
         for root, dirs, files in os.walk(srcFolderPath):
             for f in files:
                 # print("FileName : " + os.path.join(root, f))
                 srcPath = os.path.join(root, f)
                 lastIndex = root.rfind('\\')
                 curPath = root[0:lastIndex]
                 filesIndex = curPath.find('files-2.1')
                 filesLen = len('files-2.1')
                 totalLen = filesIndex + filesLen + 1
                 str_1 = curPath[0:totalLen]
                 str_1 = str_1.replace("Test", "Temp")
    
                 remainStr = curPath[totalLen:]
                 firstIndex = remainStr.find('\\')
                 str_3 = remainStr[firstIndex + 1:]
                 firstStr = remainStr[0:firstIndex]
                 str_2 = firstStr.replace('.', '\\')
                 destPath = str_1 + str_2 + "\\" + str_3
                 destPath = destPath.replace("\\", '/')
    
                 destPath = destPath.replace(srcFolderPath, dstFolderPath)
                 dataInfo = DataInfo(destPath, srcPath)
                 FilesList.append(dataInfo)
    
         for item in FilesList:
             if not os.path.exists(item.DestPath):
                 os.makedirs(item.DestPath)
             try:
                 shutil.copy(item.SrcPath, item.DestPath)
             except IOError as e:
                 print("Unable to copy file. %s" % e)
             except:
                 print("Unexpected error:", sys.exc_info())
    
         print("ProcessFinished...")
    
     if __name__ == '__main__':
         Start()
    

    build.gradle

     // GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN
    
     allprojects {
         buildscript {
             repositories {
                 //google()
                 //jcenter()
                maven {
                     // 本地路径
                    url 'D:/Android/repository'
                    metadataSources{
                        mavenPom()
                    }
                }
             }
    
             dependencies {
                 classpath 'com.android.tools.build:gradle:4.0.1'
    
             }
         }
    
         repositories {
             google()
             jcenter()
            maven {
                url 'D:/Android/repository'
                metadataSources{
                    mavenPom()
                }
            }
             flatDir {
                 dirs "${project(':unityLibrary').projectDir}/libs"
             }
         }
     }
    
     task clean(type: Delete) {
         delete rootProject.buildDir
     }