Unity安卓打包签名失败深度解析:从“密码无效”到工程迁移的完整修复指南
如果你曾经从GitHub下载过Unity项目,或者在团队协作中接手过别人的工程,大概率遇到过这个让人头疼的报错:Unable to sign the application; please provide passwords!。这个看似简单的Keystore密码错误提示,背后往往隐藏着更复杂的配置问题。我见过不少开发者在这个问题上耗费数小时,甚至因此延误了项目进度。今天,我们就来彻底拆解这个问题的根源,并提供一套从快速排查到根本解决的完整方案。
这个问题的核心其实不只是密码本身,而是Unity工程中Keystore配置的路径映射和环境一致性问题。尤其是在团队协作或工程迁移的场景下,原始开发者本地的绝对路径在另一台机器上完全失效,导致构建系统找不到Keystore文件,从而抛出密码错误的误导信息。接下来,我将带你从原理到实践,一步步构建起解决这个问题的完整知识体系。
1. 理解Keystore签名机制:为什么密码“无效”?
在深入解决之前,我们需要先理解Unity安卓打包的签名流程。Android应用签名是发布到Google Play商店的必需步骤,它确保了应用的完整性和开发者身份验证。Unity通过Java的keytool工具来管理Keystore,而问题往往出现在这个交互环节。
1.1 Keystore的基本构成
一个标准的Keystore文件包含以下关键信息:
- Keystore文件本身:通常以
.keystore或.jks为扩展名的二进制文件 - Keystore密码:保护整个Keystore文件的密码
- Key Alias(密钥别名):Keystore中特定密钥的标识符
- Key密码:保护特定密钥的密码(可能与Keystore密码相同或不同)
在Unity中,这些信息存储在ProjectSettings/ProjectSettings.asset这个YAML格式的配置文件中。当你看到密码无效的错误时,实际上可能是以下任何一种情况:
- Keystore文件路径错误或文件不存在
- Keystore密码确实错误
- Key Alias名称错误
- Key密码错误
- 文件权限问题导致无法读取
- 文件损坏或格式不兼容
1.2 Unity中的配置存储方式
Unity将Keystore配置以相对或绝对路径的形式存储在项目设置中。这里有一个关键细节:当使用绝对路径时,工程迁移到其他机器必然失败。让我们看看实际的配置结构:
PlayerSettings:
Android:
keystoreName: "C:/Users/YourName/Android/your.keystore" # 绝对路径 - 问题根源!
keystorePass: "your_keystore_password"
keyaliasName: "your_alias"
keyaliasPass: "your_key_password"
注意:上面的绝对路径配置在团队协作中是“致命”的。每个团队成员的本地路径不同,导致构建时无法找到Keystore文件。
1.3 错误信息的误导性
为什么文件找不到会报“密码无效”?这是因为Unity的构建流程中,首先尝试加载Keystore文件,如果文件不存在或路径错误,它会假设文件存在但密码错误。这种设计虽然不够友好,但理解了这一点后,我们就能更准确地定位问题。
2. 三步快速诊断法:精准定位问题根源
当遇到签名错误时,不要盲目尝试各种密码。按照以下系统化的诊断流程,可以快速定位问题所在。
2.1 第一步:检查Keystore配置状态
首先,我们需要确认Unity中当前的Keystore配置状态。打开Unity编辑器,按照以下路径检查:
- File → Build Settings → 选择Android平台 → 点击Player Settings
- 在Inspector窗口中找到 Publishing Settings 区域
- 查看Keystore配置部分
这里你会看到几个关键字段的状态:
| 字段 | 正常状态 | 问题状态 | 可能原因 |
|---|---|---|---|
| Keystore | 显示有效路径 | 显示"None"或红色警告 | 文件丢失、路径错误 |
| Keystore Password | 显示为圆点 | 显示为明文或空白 | 密码未设置或错误 |
| Key Alias | 显示别名 | 显示"None" | 别名未选择或不存在 |
| Key Password | 显示为圆点 | 显示为明文或空白 | 密码未设置或错误 |
如果任何字段显示异常,特别是Keystore字段显示为"None",那么问题很可能是路径失效而非密码错误。
2.2 第二步:验证Keystore文件的物理存在
无论Unity配置如何,我们都需要从文件系统层面验证Keystore的实际状态。打开终端或命令提示符,执行以下命令:
# 首先导航到Unity项目根目录
cd /path/to/your/unity/project
# 检查Keystore文件是否存在
ls -la Assets/Plugins/Android/ # 常见位置1
ls -la UserSettings/ # 常见位置2
ls -la . # 项目根目录
# 如果找到keystore文件,验证其完整性
keytool -list -v -keystore "找到的keystore路径" -storepass "你的密码"
如果keytool命令执行成功,你会看到类似这样的输出:
Keystore type: PKCS12
Keystore provider: SUN
Your keystore contains 1 entry
Alias name: your_alias
Creation date: Jan 15, 2023
Entry type: PrivateKeyEntry
Certificate chain length: 1
Certificate[1]:
Owner: CN=Your Name, OU=Development, O=Your Company, L=City, ST=State, C=US
Issuer: CN=Your Name, OU=Development, O=Your Company, L=City, ST=State, C=US
Serial number: 1234567890abcdef
Valid from: Mon Jan 15 10:00:00 UTC 2023 until: Fri Jan 14 10:00:00 UTC 2043
Certificate fingerprints:
SHA1: 12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78
SHA256: 12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF
如果命令失败,可能是以下原因之一:
- 文件路径错误:Keystore文件不在指定位置
- 密码错误:提供的密码不正确
- 文件损坏:Keystore文件已损坏
- 格式不兼容:Keystore类型不被当前Java版本支持
2.3 第三步:检查Unity项目配置文件
这是最关键的一步,特别是对于从Git下载或团队共享的项目。我们需要直接检查Unity的项目配置文件:
# 在项目根目录下查看ProjectSettings.asset文件
# 注意:这是一个YAML格式文件,可以用任何文本编辑器打开
# 查找Android相关的Keystore配置
grep -n "Android\|keystore\|keyalias" ProjectSettings/ProjectSettings.asset
重点关注以下配置项:
AndroidKeystoreName:
m_Value: "C:\\Users\\OriginalDev\\Android\\myapp.keystore" # 问题所在!
AndroidKeystorePass:
m_Value: 1:encrypted_password_here
AndroidKeyaliasName:
m_Value: "my_alias"
AndroidKeyaliasPass:
m_Value: 1:encrypted_password_here
如果看到类似上面的绝对路径,这就是问题的根源。在团队协作中,每个开发者的C:\Users\OriginalDev\路径都不存在,导致构建失败。
3. 工程迁移与团队协作的完整解决方案
对于团队项目或从Git下载的工程,我们需要建立一套健壮的Keystore管理策略。以下是经过实践验证的最佳方案。
3.1 方案一:使用相对路径(推荐)
最可靠的方案是将Keystore文件放在项目目录内,并使用相对路径引用。以下是具体步骤:
-
创建项目专用的Keystore目录
在项目根目录下创建专用文件夹:
mkdir -p Assets/Plugins/Android/Keystores -
将Keystore文件移动到项目内
将现有的
.keystore或.jks文件复制到上述目录。 -
更新Unity配置
在Unity编辑器中重新配置:
- 打开Player Settings → Publishing Settings
- 点击"Browse"选择项目内的Keystore文件
- 重新输入密码和别名信息
-
验证配置已更新
再次检查
ProjectSettings.asset文件,确认路径已变为相对路径:AndroidKeystoreName: m_Value: "Assets/Plugins/Android/Keystores/myapp.keystore" # 相对路径!
3.2 方案二:环境变量与自动化配置
对于需要频繁在不同环境构建的项目,可以使用环境变量动态配置Keystore路径。这种方法特别适合CI/CD流水线。
首先,创建一个配置脚本(如configure_keystore.sh):
#!/bin/bash
# configure_keystore.sh - 自动配置Keystore环境
# 设置环境变量
export UNITY_KEYSTORE_PATH="${UNITY_KEYSTORE_PATH:-./default.keystore}"
export UNITY_KEYSTORE_PASS="${UNITY_KEYSTORE_PASS:-default_password}"
export UNITY_KEYALIAS_NAME="${UNITY_KEYALIAS_NAME:-my_alias}"
export UNITY_KEYALIAS_PASS="${UNITY_KEYALIAS_PASS:-alias_password}"
# 使用Unity命令行工具更新项目配置
# 注意:这需要Unity命令行工具支持
/Applications/Unity/Hub/Editor/2022.3.0f1/Unity.app/Contents/MacOS/Unity \
-batchmode \
-nographics \
-quit \
-executeMethod EditorBuildScript.UpdateKeystoreSettings \
-projectPath "$(pwd)"
然后,创建一个C#编辑器脚本来自动化配置:
// Editor/KeystoreConfigurator.cs
using UnityEditor;
using UnityEngine;
using System.IO;
public static class EditorBuildScript
{
[MenuItem("Tools/Configure Keystore from Environment")]
public static void UpdateKeystoreSettings()
{
string keystorePath = System.Environment.GetEnvironmentVariable("UNITY_KEYSTORE_PATH");
string keystorePass = System.Environment.GetEnvironmentVariable("UNITY_KEYSTORE_PASS");
string keyaliasName = System.Environment.GetEnvironmentVariable("UNITY_KEYALIAS_NAME");
string keyaliasPass = System.Environment.GetEnvironmentVariable("UNITY_KEYALIAS_PASS");
if (!string.IsNullOrEmpty(keystorePath) && File.Exists(keystorePath))
{
PlayerSettings.Android.keystoreName = keystorePath;
PlayerSettings.Android.keystorePass = keystorePass;
PlayerSettings.Android.keyaliasName = keyaliasName;
PlayerSettings.Android.keyaliasPass = keyaliasPass;
Debug.Log($"Keystore配置已更新: {keystorePath}");
AssetDatabase.SaveAssets();
}
else
{
Debug.LogWarning("环境变量未设置或Keystore文件不存在,使用项目内配置");
}
}
}
3.3 方案三:Git忽略与安全存储
对于团队项目,Keystore文件绝对不能提交到版本控制系统。正确的做法是:
-
将Keystore添加到.gitignore
# .gitignore 添加以下内容 *.keystore *.jks Assets/Plugins/Android/Keystores/*.keystore Assets/Plugins/Android/Keystores/*.jks -
提供配置模板
创建
keystore-config.template文件,团队成员可以基于此创建自己的配置:# keystore-config.template # 复制此文件为 keystore-config.local.yaml 并填写实际值 keystore: path: "Assets/Plugins/Android/Keystores/YOUR_KEYSTORE.keystore" password: "YOUR_KEYSTORE_PASSWORD" alias: "YOUR_ALIAS" alias_password: "YOUR_ALIAS_PASSWORD" -
使用安全的密码管理
对于生产环境的Keystore,考虑使用以下安全方案:
- CI/CD系统密钥管理:如GitHub Secrets、GitLab CI Variables
- 密码管理器:如1Password、LastPass的团队共享功能
- 硬件安全模块:对于企业级安全要求
4. 高级排查:当基础方案无效时
如果上述方案都不能解决问题,可能是更复杂的情况。以下是高级排查步骤。
4.1 检查Java环境与keytool版本
Unity使用系统安装的Java环境中的keytool。版本不匹配可能导致兼容性问题:
# 检查Java版本
java -version
# 检查keytool位置和版本
which keytool
keytool -version
# 验证keytool能否正常处理Keystore
keytool -list -keystore "你的keystore文件" -storepass "测试密码" 2>&1
常见问题包括:
- Java版本过旧:低于Java 8可能有问题
- 多个Java版本冲突:系统中有多个Java,Unity使用了错误的版本
- keytool路径不在PATH中:Unity找不到keytool
4.2 文件权限与所有权问题
在Linux/macOS系统上,文件权限问题可能导致Unity无法读取Keystore:
# 检查文件权限
ls -la "你的keystore文件"
# 确保Unity进程有读取权限
# Unity通常以当前用户身份运行,确保用户有读取权限
chmod 644 "你的keystore文件" # 设置适当权限
# 检查文件所有权
stat "你的keystore文件"
4.3 特殊字符与路径编码
路径中的特殊字符或空格可能导致问题:
# 检查路径中是否包含特殊字符
echo "你的keystore路径" | od -c
# 临时重命名测试(移除特殊字符和空格)
cp "原路径/包含 空 格.keystore" "测试路径/简单名称.keystore"
# 在Unity中测试新路径
如果路径包含空格,在Unity配置中可能需要转义或使用短路径格式(Windows)。
4.4 Unity版本特定问题
某些Unity版本存在已知的Keystore相关bug。检查Unity官方论坛和issue tracker:
| Unity版本 | 已知问题 | 解决方案 |
|---|---|---|
| 2019.2.x | Keystore路径处理bug | 升级到2019.3+或使用相对路径 |
| 2020.1.x | 密码加密存储问题 | 清除Library文件夹后重新配置 |
| 2021.2.x | 与特定Java版本不兼容 | 使用Java 8或Java 11 |
可以通过以下命令检查Unity版本和已知问题:
# 查看Unity版本
/Applications/Unity/Hub/Editor/当前版本/Unity.app/Contents/MacOS/Unity -version
# 或者查看ProjectSettings/ProjectVersion.txt
cat ProjectSettings/ProjectVersion.txt
5. 预防措施与最佳实践
解决当前问题很重要,但预防未来出现问题更为关键。以下是我在多个项目中总结的最佳实践。
5.1 项目初始化检查清单
在新项目或新成员加入时,执行以下检查:
-
Keystore配置验证脚本
创建自动化验证脚本
verify_keystore.py:#!/usr/bin/env python3 import os import subprocess import sys def check_keystore_config(): """检查Unity项目中的Keystore配置""" project_settings = "ProjectSettings/ProjectSettings.asset" if not os.path.exists(project_settings): print("错误: 未找到ProjectSettings.asset文件") return False with open(project_settings, 'r') as f: content = f.read() # 检查Android配置部分 if "AndroidKeystoreName" not in content: print("警告: 未找到Android Keystore配置") return True # 可能不需要Keystore # 提取Keystore路径 import re path_match = re.search(r'AndroidKeystoreName:\s*m_Value:\s*"([^"]+)"', content) if path_match: keystore_path = path_match.group(1) print(f"Keystore路径: {keystore_path}") # 检查是否为绝对路径 if os.path.isabs(keystore_path): print("⚠️ 警告: 使用绝对路径,团队协作可能有问题") print("建议改为相对路径,如: Assets/Plugins/Android/yourapp.keystore") return False # 检查文件是否存在 full_path = os.path.join(os.path.dirname(project_settings), "..", keystore_path) full_path = os.path.normpath(full_path) if os.path.exists(full_path): print("✅ Keystore文件存在") return True else: print(f"❌ Keystore文件不存在: {full_path}") return False return True if __name__ == "__main__": if check_keystore_config(): print("Keystore配置检查通过") sys.exit(0) else: print("Keystore配置存在问题,请修复") sys.exit(1) -
团队协作文档
在项目README中明确Keystore管理规范:
## Android签名配置 ### 开发环境配置 1. 将your-app.keystore文件放置在 `Assets/Plugins/Android/Keystores/` 目录 2. 在Unity中配置Publishing Settings: - Keystore: 选择上述文件 - Passwords: 从团队密码管理器获取 ### CI/CD配置 环境变量: - UNITY_KEYSTORE_BASE64: Base64编码的keystore文件 - UNITY_KEYSTORE_PASS: Keystore密码 - UNITY_KEYALIAS_NAME: 密钥别名 - UNITY_KEYALIAS_PASS: 密钥密码
5.2 自动化构建流水线集成
对于持续集成环境,建议以下配置:
# .github/workflows/build-android.yml
name: Build Android APK
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Keystore from Secrets
run: |
# 从GitHub Secrets解码Keystore
echo "${{ secrets.UNITY_KEYSTORE_BASE64 }}" | base64 --decode > android.keystore
# 验证Keystore
keytool -list -keystore android.keystore -storepass "${{ secrets.UNITY_KEYSTORE_PASS }}" || exit 1
# 移动到项目目录
mkdir -p Assets/Plugins/Android/Keystores
mv android.keystore Assets/Plugins/Android/Keystores/
- name: Build with Unity
uses: game-ci/unity-builder@v2
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
targetPlatform: Android
androidKeystoreName: Assets/Plugins/Android/Keystores/android.keystore
androidKeystorePass: ${{ secrets.UNITY_KEYSTORE_PASS }}
androidKeyaliasName: ${{ secrets.UNITY_KEYALIAS_NAME }}
androidKeyaliasPass: ${{ secrets.UNITY_KEYALIAS_PASS }}
5.3 定期维护与轮换策略
安全最佳实践要求定期轮换签名密钥:
-
密钥有效期监控
# 检查密钥过期时间 keytool -list -v -keystore your.keystore -storepass yourpassword | grep -A 2 "Valid from" -
密钥轮换计划表
任务 频率 负责人 完成标准 检查密钥有效期 每季度 技术负责人 确认有效期>1年 测试新密钥构建 每年 开发团队 新APK可正常安装运行 更新CI/CD配置 密钥轮换时 DevOps工程师 所有流水线通过 文档更新 配置变更时 技术文档负责人 README和Wiki同步更新
6. 疑难案例分析与解决
在实际开发中,我遇到过一些特别棘手的案例,以下是其中两个典型问题的解决方案。
6.1 案例一:Git LFS导致的Keystore文件损坏
问题现象:团队使用Git LFS管理大文件,Keystore文件被意外添加到LFS跟踪,导致文件指针化而非实际存储。
解决方案:
# 1. 从LFS中移除Keystore文件
git lfs untrack "*.keystore"
git lfs untrack "*.jks"
# 2. 从缓存中清除
git rm --cached Assets/Plugins/Android/Keystores/*.keystore
# 3. 重新添加实际文件
git add Assets/Plugins/Android/Keystores/*.keystore -f
# 4. 更新.gitattributes
echo "*.keystore binary" >> .gitattributes
echo "*.jks binary" >> .gitattributes
# 5. 提交更改
git commit -m "fix: 将Keystore文件从LFS移出"
6.2 案例二:多模块项目的签名冲突
问题现象:项目包含多个Android Library模块,每个模块尝试单独签名,导致冲突。
解决方案:统一签名配置,在根项目的gradle.properties中配置:
# gradle.properties
# 禁用所有模块的自动签名
android.enableBuildCache=false
# 主模块签名配置(通过环境变量传递)
RELEASE_STORE_FILE=${RELEASE_STORE_FILE}
RELEASE_STORE_PASSWORD=${RELEASE_STORE_PASSWORD}
RELEASE_KEY_ALIAS=${RELEASE_KEY_ALIAS}
RELEASE_KEY_PASSWORD=${RELEASE_KEY_PASSWORD}
在主模块的build.gradle中:
android {
signingConfigs {
release {
if (project.hasProperty('RELEASE_STORE_FILE')) {
storeFile file(RELEASE_STORE_FILE)
storePassword RELEASE_STORE_PASSWORD
keyAlias RELEASE_KEY_ALIAS
keyPassword RELEASE_KEY_PASSWORD
}
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
在Unity中,通过Post-Process Build脚本同步配置:
// Assets/Editor/AndroidPostProcess.cs
using UnityEditor;
using UnityEditor.Android;
using System.IO;
public class AndroidKeystorePostProcess : IPostGenerateGradleAndroidProject
{
public int callbackOrder { get { return 0; } }
public void OnPostGenerateGradleAndroidProject(string path)
{
string gradlePropertiesPath = Path.Combine(path, "../gradle.properties");
if (File.Exists(gradlePropertiesPath))
{
string content = File.ReadAllText(gradlePropertiesPath);
// 添加或更新签名配置
content += $@"
RELEASE_STORE_FILE={PlayerSettings.Android.keystoreName}
RELEASE_STORE_PASSWORD={PlayerSettings.Android.keystorePass}
RELEASE_KEY_ALIAS={PlayerSettings.Android.keyaliasName}
RELEASE_KEY_PASSWORD={PlayerSettings.Android.keyaliasPass}
";
File.WriteAllText(gradlePropertiesPath, content);
Debug.Log("Gradle properties updated with signing config");
}
}
}
7. 工具与脚本库
为了简化日常工作中的Keystore管理,我整理了一套实用工具脚本,你可以直接用在项目中。
7.1 Keystore验证工具
// Assets/Editor/KeystoreValidator.cs
using UnityEditor;
using UnityEngine;
using System.Diagnostics;
using System.IO;
using System.Text;
public class KeystoreValidator : EditorWindow
{
private string keystorePath = "";
private string keystorePassword = "";
private string keyAlias = "";
private string keyPassword = "";
private string validationResult = "";
[MenuItem("Tools/Keystore/验证工具")]
public static void ShowWindow()
{
GetWindow<KeystoreValidator>("Keystore验证工具");
}
void OnGUI()
{
GUILayout.Label("Keystore配置验证", EditorStyles.boldLabel);
keystorePath = EditorGUILayout.TextField("Keystore路径", keystorePath);
keystorePassword = EditorGUILayout.PasswordField("Keystore密码", keystorePassword);
keyAlias = EditorGUILayout.TextField("密钥别名", keyAlias);
keyPassword = EditorGUILayout.PasswordField("密钥密码", keyPassword);
if (GUILayout.Button("验证配置"))
{
ValidateKeystore();
}
if (GUILayout.Button("从项目设置加载"))
{
LoadFromProjectSettings();
}
if (!string.IsNullOrEmpty(validationResult))
{
EditorGUILayout.HelpBox(validationResult,
validationResult.Contains("成功") ? MessageType.Info : MessageType.Error);
}
if (GUILayout.Button("导出安全配置(用于CI/CD)"))
{
ExportSecureConfig();
}
}
void LoadFromProjectSettings()
{
keystorePath = PlayerSettings.Android.keystoreName;
keystorePassword = PlayerSettings.Android.keystorePass;
keyAlias = PlayerSettings.Android.keyaliasName;
keyPassword = PlayerSettings.Android.keyaliasPass;
}
void ValidateKeystore()
{
if (string.IsNullOrEmpty(keystorePath) || !File.Exists(keystorePath))
{
validationResult = "错误: Keystore文件不存在";
return;
}
try
{
Process process = new Process();
process.StartInfo.FileName = "keytool";
process.StartInfo.Arguments = $"-list -v -keystore \"{keystorePath}\" -storepass \"{keystorePassword}\"";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
StringBuilder output = new StringBuilder();
process.OutputDataReceived += (sender, args) => output.AppendLine(args.Data);
process.Start();
process.BeginOutputReadLine();
process.WaitForExit(5000);
if (process.ExitCode == 0)
{
string result = output.ToString();
if (result.Contains(keyAlias))
{
validationResult = "✅ 验证成功: Keystore和别名有效";
}
else
{
validationResult = "⚠️ 警告: Keystore有效但未找到指定别名";
}
}
else
{
validationResult = "❌ 验证失败: 密码错误或文件损坏";
}
}
catch (System.Exception e)
{
validationResult = $"❌ 执行错误: {e.Message}\n确保已安装Java并正确配置PATH";
}
}
void ExportSecureConfig()
{
string config = $@"# 安全配置 - 请妥善保管
KEYSTORE_PATH={keystorePath}
KEYSTORE_PASSWORD={keystorePassword}
KEY_ALIAS={keyAlias}
KEY_PASSWORD={keyPassword}
# 注意: 此文件包含敏感信息,不要提交到版本控制";
string path = EditorUtility.SaveFilePanel("导出配置", "", "keystore-config.env", "env");
if (!string.IsNullOrEmpty(path))
{
File.WriteAllText(path, config);
EditorUtility.DisplayDialog("导出成功", "配置已导出到安全位置", "确定");
}
}
}
7.2 自动化修复脚本
对于常见的路径问题,这个脚本可以自动修复:
// Assets/Editor/KeystorePathFixer.cs
using UnityEditor;
using UnityEngine;
using System.IO;
public class KeystorePathFixer
{
[MenuItem("Tools/Keystore/自动修复路径问题")]
public static void FixKeystorePaths()
{
string projectPath = Application.dataPath.Replace("/Assets", "");
string projectSettingsPath = Path.Combine(projectPath, "ProjectSettings/ProjectSettings.asset");
if (!File.Exists(projectSettingsPath))
{
Debug.LogError("找不到ProjectSettings.asset文件");
return;
}
string[] lines = File.ReadAllLines(projectSettingsPath);
bool modified = false;
for (int i = 0; i < lines.Length; i++)
{
// 查找Android Keystore配置
if (lines[i].Contains("AndroidKeystoreName:"))
{
// 找到路径行
if (i + 1 < lines.Length && lines[i + 1].Contains("m_Value:"))
{
string pathLine = lines[i + 1];
string oldPath = ExtractPath(pathLine);
if (!string.IsNullOrEmpty(oldPath))
{
// 检查是否为绝对路径
if (Path.IsPathRooted(oldPath) && !oldPath.StartsWith(projectPath))
{
Debug.Log($"检测到外部绝对路径: {oldPath}");
// 提示用户选择新位置
string newRelativePath = EditorUtility.SaveFilePanel(
"选择Keystore保存位置",
"Assets/Plugins/Android/Keystores",
"app.keystore",
"keystore");
if (!string.IsNullOrEmpty(newRelativePath))
{
// 转换为相对于项目的路径
string relativePath = "Assets" + newRelativePath.Replace(projectPath, "");
// 复制文件
if (File.Exists(oldPath))
{
File.Copy(oldPath, newRelativePath, true);
Debug.Log($"已复制Keystore文件到: {newRelativePath}");
}
// 更新配置
lines[i + 1] = $" m_Value: {relativePath}";
modified = true;
Debug.Log($"已更新Keystore路径为: {relativePath}");
}
}
}
}
}
}
if (modified)
{
File.WriteAllLines(projectSettingsPath, lines);
AssetDatabase.Refresh();
Debug.Log("项目设置已更新,请重新打开Unity使更改生效");
}
else
{
Debug.Log("未发现需要修复的路径问题");
}
}
private static string ExtractPath(string line)
{
int start = line.IndexOf('"');
int end = line.LastIndexOf('"');
if (start >= 0 && end > start)
{
return line.Substring(start + 1, end - start - 1);
}
return null;
}
}
这些工具和脚本来自实际项目经验,能够处理大多数常见的Keystore配置问题。关键是要理解问题的本质——通常不是密码错误,而是配置的环境依赖性问题。通过建立规范的流程和使用自动化工具,可以彻底避免这类问题影响开发进度。

500

被折叠的 条评论
为什么被折叠?



