介绍

Unity 构建 Android 包之后需要执行签名,如果整个构建流程在 Unity 中执行,那么签名过程也需要在 Unity 中执行。

需求

  • 需要同时支持 Windows 与 macOS 打包
  • jarsigner 无法正确签名 Android 应用,需要使用 apksigner
  • apksigner 不是每个 Android SDK 版本都带,需要选择最新版本中的执行

尝试

符号链接

由于环境变量中没有 SDK 的路径,在 macOS 中尝试将 apksigner 符号链接到 /usr/bin 中,提示

1
2
3
$ sudo ln /Library/android-sdk-macosx/build-tools/26.0.2/apksigner /usr/bin/apksigner

ln: /usr/bin/apksigner: Operation not permitted

bash - Cannot create a symlink inside of /usr/bin even as sudo - Stack Overflow

解决方案

环境变量

macOS 下无法将 Finder 下启动的程序增加额外的 PATH 环境变量

因此尝试在构建时手动指定环境变量,注意 Windows 与 macOS 分隔符有区别,分别为 ; :

How do I view/see the PATH in a windows environment?

虽然 Windows 平台下 apksigner 文件名为 apksigner.bat,但是 bat 在 Windows 中是可执行文件,可以忽略扩展名执行,因此执行代码可以不用修改。

另外有一个问题:

Note that apksigner is missing in version 26.0.0 of build-tools. It is tracked in issuetracker.google.com/issues/62696222 and supposed to be fixed in the next version. The workaround until then is to use apksigner from 25.0.3. – friederbluemle Jun 29 ‘17 at 2:50 Update: apksigner is included in version 26.0.1 – forresthopkinsa Sep 25 ‘17 at 18:07

How do I verify that an Android apk is signed with a release certificate? - Stack Overflow

所以在添加环境变量时,是将 build-tools 目录下的目录反序添加到环境变量中,优先使用高版本的工具。

在调用 apksigner 时建议将所有需要的参数显式指定,防止出错。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public static void Sign()
{
    AddBuildToolsToPath();

    DoSign();
}

private static void AddBuildToolsToPath()
{
    string sdkRoot = EditorPrefs.GetString("AndroidSdkRoot");
    string[] levels = Directory.GetDirectories(Path.Combine(sdkRoot, "build-tools"));
    // 优先使用高版本 SDK
    System.Array.Reverse(levels);

    #if UNITY_EDITOR_WIN
    string delimiter = ";";
    #else
    string delimiter = ":";
    #endif

    var name = "PATH";
    string PATH = System.Environment.GetEnvironmentVariable(name);
    var value = PATH + delimiter + string.Join(delimiter, levels);
    var target = System.EnvironmentVariableTarget.Process;
    System.Environment.SetEnvironmentVariable(name, value, target);
}

private static void DoSign()
{
    var process = new System.Diagnostics.Process();
    process.StartInfo.FileName = "apksigner";
    process.StartInfo.Arguments = string.Format("sign --ks {0} --ks-pass pass:{1} --ks-key-alias {2} --key-pass pass:{3} --in {4} --out {5}",
        "application.keystore",
        PlayerSettings.Android.keystorePass,
        PlayerSettings.Android.keyaliasName,
        PlayerSettings.Android.keyaliasPass,
        "unsigned.apk", "signed.apk");
    process.Start();
    process.WaitForExit();
}

参考资料