Jenkins 构建 Unity打包APK
一、创建一个 Pipeline 任务
在项目跟目录创建 Pipeline 脚本 jenkins_scripts\Pipeline\android_master_pipeline
脚本如下
// Android Master 打包 apk
pipeline {
agent any
stages {
stage('Test Parameter') {
steps {
script {
// shell 脚本目录
ANDROID_MASTER_SHELL_PATH="${env.WORKSPACE}/jenkins_scripts/shell/android_master.sh"
// 给 shell 脚本添加权限并执行
sh "chmod +x ${ANDROID_MASTER_SHELL_PATH} && ${ANDROID_MASTER_SHELL_PATH}"
}
}
}
}
}
在项目跟目录创建 Shell 脚本 jenkins_scripts\shell\android_master.sh
#!/bin/sh
#!/bin/bash
echo "this is android_master.sh"
# 输出工作目录
echo "WORKSPACE=${WORKSPACE}"
# Unity 安装目录
UNITY_PATH=/Applications/Unity/Hub/Editor/2022.3.26f1/Unity.app/Contents/MacOS/Unity
# Unity 项目目录 Assets、Library、ProjectSettings 文件夹在 PROJECT_PATH 路径下
PROJECT_PATH="${WORKSPACE}/Project"
# 通过 export 将变量标记为环境变量,并传递给 Unity 使用
# 导出到 Unity 后都是 字符串
# 在 Unity 中通过 string value = Environment.GetEnvironmentVariable(key); 获取
# bool 类型的传递过去是字符串 "true" 和 "false"
export WORKSPACE_PATH="${WORKSPACE}"
# 生成文件保存目录,也通过 export 传递给 Unity
export EXPORT_PATH="${WORKSPACE}/Export"
export KEY_STORE_PATH="${WORKSPACE}/jenkins_scripts/Tools/user.keystore"
# 生成的 apk 名字
export APK_NAME="${JOB_BASE_NAME}_${BUILD_ID}_${BRANCH_NAME}.apk"
# 生成的 apk 路径
export EXPORT_APK_PATH="${EXPORT_PATH}/${APK_NAME}"
# 生成 .aab 文件
export BUILD_AAB="false"
echo "UNITY_PATH=${UNITY_PATH}"
echo "PROJECT_PATH=${PROJECT_PATH}"
echo "APK_NAME=${APK_NAME}"
echo "EXPORT_APK_PATH=${EXPORT_APK_PATH}"
echo "BUILD_AAB=${BUILD_AAB}"
# 下面是调用 Unity 的命令
# 在 Assets 文件夹下任意目录 创建文件夹 Editor
# 新建 ProjectExportApk.cs 删除继承 MonoBehaviour
# 添加一个 public static void ExportAPK() 方法
# 下面命令通过 ProjectExportApk.ExportAPK 调用
$UNITY_PATH -projectPath $PROJECT_PATH \
-buildTarget android \
-executeMethod ProjectExportApk.ExportAPK \
-logfile - \
-batchMode -quit \
-GMMode
echo "this is testShellUnity.sh end"
二、配置 Unity 项目
打开 Unity 项目
Project Settings -> Player -> Android -> Other settings 修改配置
下面只说几个比较关键的配置
-
Identification
- Package Name:Android 应用包名
- Minimum API Level:支持的 Android 以及 API 最低等级
- Target API Level:目标 API 等级,选择最高的即可如现在最高的是 API Level 35
-
Configuration
- Scripting Backend:IL2Cpp
- Api Compatibility Level:.NET Framework
- IL2CPP Code Generation:Faster runtime
- C++ Compiler Configuration:Release
- Use incremental GC:勾选
- Target Architectures:安装包CUP 目标架构
- ARMv7:勾选
- ARM64:勾选
-
Optimization:
- Managed Stripping Level:代码裁剪剔除等级,最好选 High
-
Publishing Settings
- Keystore Manager…:Keystore 文件管理,可以从这里创建一个,记录创建 Keystore 文件时配置的参数以及 Keystore密码,Alias 对应的密码
- Custom Keystore:勾选,使用自定义 Keystore 文件,配置一个为这个 Android 项目创建的 Keystore 文件
- Password:对应 Keystore 文件对应的密码
- Project Key
- Alias:创建 Keystore 输出的 Alias 参数
- Password:Alias 对应的密码
-
Build
- Custom Main Manifest:勾选
- Custom Launcher Manifest:不勾选
- Custom Main Gradle Template:勾选
- Custom Launcher Gradle Tmplate:勾选
- Custom Base Gradel Template:勾选
- Custom Gradle Properties Template:勾选
- Custom Gradle Settings Template:勾选
- Custom Proguard File:不勾选
勾选后保存,Unity 自动在 Assets/Plugins/Android 目录下生成各自对应的文件
三、C# 生成 APK 代码
Assets 目录下任意层级下创建 Editor 文件夹,创建 ProjectExportApk.cs
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System.IO;
public class ProjectExportApk : Editor
{
private static BuildOptions s_BuildOptions = BuildOptions.CompressWithLz4HC;
[MenuItem("Tools/ExportAPK")]
public static void ExportAPK()
{
Debug.Log("ExportApk ExportAPK start");
// 切换平台到 Android 分支
bool switchAndroid = EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
if (!switchAndroid)
{
Debug.LogError("ExportApk Switch Android Error");
return;
}
Debug.Log("ExportApk Switch Android success");
string exportPath = WorkExportPath();
if (Directory.Exists(exportPath))
{
Directory.Delete(exportPath, true);
}
Directory.CreateDirectory(exportPath);
PlayerSettings.applicationIdentifier = "com.DeCompany.Project";
string keystorePath = GetKeyStorePath();
Debug.Log("keystorePath:" + keystorePath);
// 配置 keystore 信息
PlayerSettings.Android.keystoreName = keystorePath;
PlayerSettings.Android.keystorePass = "123456";
PlayerSettings.Android.keyaliasName = "testapk";
PlayerSettings.Android.keyaliasPass = "123456";
PlayerSettings.Android.useCustomKeystore = true;
PlayerSettings.Android.bundleVersionCode = 2;
PlayerSettings.Android.useAPKExpansionFiles = false;
EditorUserBuildSettings.buildAppBundle = EnvironmentUtil.GetBool("BUILD_AAB", false);
// 生成符号文件
EditorUserBuildSettings.androidCreateSymbols = AndroidCreateSymbols.Public;
EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
var options = s_BuildOptions;
// 是否连接 profiler
bool connectProfiler = false;
if (connectProfiler)
{
options |= BuildOptions.Development;
EditorUserBuildSettings.development = true;
EditorUserBuildSettings.connectProfiler = true;
EditorUserBuildSettings.buildWithDeepProfilingSupport = true;
}
EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
PlayerSettings.bundleVersion = "1.1.1";
PlayerSettings.productName = "TestProduct";
var targetArchitectures = AndroidArchitecture.ARM64 | AndroidArchitecture.ARMv7;
PlayerSettings.Android.targetArchitectures = (AndroidArchitecture)targetArchitectures;
// PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, defs);
List<string> levels = new List<string>();
foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
{
if (!scene.enabled) continue;
// 获取有效的 Scene
levels.Add(scene.path);
}
string apkPath = GetApkPath();
Debug.Log("apkPath:" + apkPath);
BuildPipeline.BuildPlayer(levels.ToArray(), apkPath, BuildTarget.Android, options);
}
public static string WorkExportPath()
{
return EnvironmentUtil.GetString("EXPORT_PATH", Application.dataPath);
}
private static string GetKeyStorePath()
{
return EnvironmentUtil.GetString("KEY_STORE_PATH", string.Empty);
}
private static string GetApkPath()
{
return EnvironmentUtil.GetString("EXPORT_APK_PATH", "output.apk");
}
}
上面代码中用到的 EnvironmentUtil 类在这里 Jenkins 调用 Shell 脚本,在Shell脚本中调用 Unity 类方法,传递参数给Unity
执行 Jenkins 构建测试生成结果