Unity 多渠道打包 APK

Unity 多渠道打包 APK

一个工程打包给 N 个渠道,每个渠道有自己的 应用名、应用Icon、包名、keystore、各种需要接入的SDK,甚至每个渠道有单独的资源等等

为了方便、省事、省时,决定通过脚本执行打包,下面是自己的一个思路实践,比较简单,只包含了一个 SDK,没有根据渠道来区分SDK(需后续添加),也没有为每个渠道设置单独的资源(需后续添加)。

思路:将渠道不同需求规整到一个统一的配置表,打包时读取配置表,在代码中动态设置相应的值,最终执行打包。

抽象出来的配置表如下
这里写图片描述

目前只配置了这么几个
将 Excel 导出到 xml
Unity 多渠道打包 APK_第1张图片

定义一个 配置表对应的 C# 类 BuildConfig.cs

public class BuildConfig {
    /// 
    ///编号
    /// 
    public int id { get; private set; }
    /// 
    ///公司名
    /// 
    public string companyName { get; private set; }
    /// 
    ///应用名
    /// 
    public string productName { get; private set; }
    /// 
    ///包名
    /// 
    public string bundleIdentifier { get; private set; }
    /// 
    ///应用图标
    /// 
    public string defaultIcon { get; private set; }
    /// 
    ///版本号
    /// 
    public string bundleVersion { get; private set; }
    /// 
    ///版本号
    /// 
    public int bundleVersionCode { get; private set; }
    /// 
    ///keystore文件路径
    /// 
    public string keystorePath { get; private set; }
    /// 
    ///keystore密码
    /// 
    public string keystorePass { get; private set; }
    /// 
    ///别名
    /// 
    public string keyaliasName { get; private set; }
    /// 
    ///别名密码
    /// 
    public string keyaliasPass { get; private set; }
    /// 
    ///脚本宏定义
    /// 
    public string scriptingDefine { get; private set; }
    /// 
    ///APK名
    /// 
    public string apkName { get; private set; }

}

将 channel.xml 导入到Unity 工程,
自己写一个解析 xml 的方法,

每一行的值对应赋值给 一个 BuildConfig 对象 new BuildConfig,将每个渠道配置传入打包方法,依次打包所有渠道 APK 包

代码如下,注释比较详细不细说了

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;

public class BuildAPK : Editor {

    [MenuItem("Tool/BuildApk")]
    static void Build()
    {
        TextAsset textAsset = LoadXML.Instance.LoadTextAsset("Assets/Channel/channel.xml");

        // 读取 channel.xml 将每一行的数据赋值给 一个 BuildConfig 对象,添加到字典中
        // 最终将 BuildConfig 对象字典返回
        Dictionary buildConfigDic = LoadXML.Instance.GetTableData("channel", textAsset.text);

        // 遍历字典
        foreach(KeyValuePair kv in buildConfigDic)
        {
            // 获取每个渠道的配置
            BuildConfig _buildConfig = kv.Value;

            // 执行打包
            Build(_buildConfig);
        }
    }

    private static void Build(BuildConfig buildConfig)
    {
        // 公司名
        PlayerSettings.companyName = buildConfig.companyName;
        // 产品名
        PlayerSettings.productName = buildConfig.productName;
        // 包名
        PlayerSettings.bundleIdentifier = buildConfig.bundleIdentifier;

        // 
        PlayerSettings.bundleVersion = buildConfig.bundleVersion;
        PlayerSettings.Android.bundleVersionCode = buildConfig.bundleVersionCode;

        // keystore 路径, G:\keystore\one.keystore
        PlayerSettings.Android.keystoreName = buildConfig.keystorePath;
        // one.keystore 密码
        PlayerSettings.Android.keystorePass = buildConfig.keystorePass;

        // one.keystore 别名
        PlayerSettings.Android.keyaliasName = buildConfig.keyaliasName;
        // 别名密码
        PlayerSettings.Android.keyaliasPass = buildConfig.keyaliasPass;

        // Load 应用图标,暂时放 Resource 下了
        Texture2D icon = Resources.Load(buildConfig.defaultIcon) as Texture2D;
        // 设置应用图标
        PlayerSettings.SetIconsForTargetGroup(BuildTargetGroup.Android, new Texture2D[] { icon, icon, icon, icon, icon, icon });

        BuildTargetGroup buildTargetGroup = BuildTargetGroup.Android;
        // 设置宏定义
        PlayerSettings.SetScriptingDefineSymbolsForGroup(buildTargetGroup, buildConfig.scriptingDefine); // 宏定义

        List levels = new List();
        foreach (EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
        {
            if (!scene.enabled) continue;
            // 获取有效的 Scene
            levels.Add(scene.path);
        }

        BuildTarget buildTarget = BuildTarget.Android;
        // 切换到 Android 平台
        EditorUserBuildSettings.SwitchActiveBuildTarget(buildTarget);

        // 打包出 APK 名
        string apkName = string.Format("{0}.apk", buildConfig.apkName);
        // 执行打包
        string res = BuildPipeline.BuildPlayer(levels.ToArray(), apkName, buildTarget, BuildOptions.None);

        if (res.Length > 0)
        {
            throw new Exception("BuildPlayer failure: " + res);
        }

        AssetDatabase.Refresh();
    }

}

所有渠道 APK 使用同样的 SDK (实际项目中会有不同)
Unity 多渠道打包 APK_第2张图片

Resources\Icon 下放了几个图标
Unity 多渠道打包 APK_第3张图片

在菜单栏创建了一个 BuildApk 按钮,执行打包
这里写图片描述

打包完成在项目根目录生成了所有渠道的 APK
Unity 多渠道打包 APK_第4张图片

APK 的图标和 apk名都和配置表中对应上了,

将所有APK 同时安装到模拟器上运行测试
1.安装所有APK
Unity 多渠道打包 APK_第5张图片
6 个 APK 都可以成功安装到模拟器上

2.分别测试
Unity 多渠道打包 APK_第6张图片

Unity 多渠道打包 APK_第7张图片

Unity 多渠道打包 APK_第8张图片

不一一贴出来了。经过测试都可以成功执行

测试打包过程中遇到的一些坑

1.
图标或者应用名不是设置的,检查 AndroidManifest.xml 中 application 标签中是否添加了
应用名 android:lable
应用图标 android:icon

android:label="@string/app_name"    
android:icon="@drawable/app_icon"

Unity 多渠道打包 APK_第9张图片

PlayerSettings.SetIconsForTargetGroup 方法设置的图标,打包 APK 时,会将设置的图片自动放到 APK 中 res\drawable-mdpi-v4\app_icon.png 、 res\drawable-xhdpi-v4\app_icon.png 等文件夹中,图片名字被设置成了 app_icon.png(固定设置)

应用名 app_name 到 res\values\strings.xml 中查找
app_name 的值对应为 PlayerSettings.productName 设置的值

Unity 多渠道打包 APK_第10张图片

2.
测试中发现 点击 Speak 按钮没有调用到 SDK,speak 调用的是接入的 讯飞-语音合成SDK。点击其他几个按钮,可以正常调用(其他几个为自己写的 加、减,字符串操作,不是第三方的SDK)
查找原因发现调用方法 PlayerSettings.SetIconsForTargetGroup 会将 PlayseSetting 中Icon 项中设置为 Override for Android,这里是正确的
Unity 多渠道打包 APK_第11张图片

由于 接入 讯飞 SDK 的时候没有处理好 res 下的资源,导致Unity 打包 APK时资源编译出了问题,通过清空 SDK Android 项目中 res 文件夹下没用的资源和 配置,重新导出 aar 放入Unity 中打包 APK 测试成功了

你可能感兴趣的:(unity-android)