Unity打包指南:从APK到AAB的转换

之前写了一篇关于Unity安卓一键打包,那个是打出apk应用的,后面也说了平台要弄聚合SDK,需要我们这边出母包,也就是AAR。为了省去重复劳动(主要是我工作电脑上用Unity2017.4.7f1版本不管用Gradle打apk还是导出Android Studio工程,都会卡在done这,然后Unity就卡死了),所以专门花了一天时间研究下写了个一键生成AAR包的工程。

androidstudio unity打包apk unity打包aab_xml

其实没什么难点,核心思想就是导出Android Studio Project,这个可以用Unity自带的API来实现,然后通过IO操作修改一些文件,比如build.gradle和AndroidManifest.xml这2个文件。最后再用批处理生成AAR就行。



BuildOptions参数和输出文件夹,不然会出错的。其实还有一段是对c#脚本的IO处理,也就是修改外网包和开发包几个变量的代码,因为涉及到项目了,所以也就不放出这些相关接口。

登录后复制

public class OneKeyBuildAAR
{
    private static string androidProjectName = "test2018";
    
    private static Dictionary<string, string> andoridFiles;
    private static List<string> androidNeedUpdateFile = new List<string>() { "build.gradle", "AndroidManifest.xml" };

    [MenuItem("一键打包/一键生成AAR")]
    public static void OneKeyExportAAR()
    {
        BuildAndroidStudioProject(false);
        BuildAAR();
    }

    [MenuItem("一键打包/1. 导出Android Studio工程")]
    public static void ExportAndroidStudioProject()
    {
        BuildAndroidStudioProject(true);
    }

    [MenuItem("一键打包/2. 生成AAR")]
    public static void ExportAAR()
    {
        BuildAAR();
    }

    /// <summary>
    /// 导出Android Studio工程
    /// </summary>
    /// <param name="bIsOpenDirectory">是否自动打开文件夹</param>
    /// <returns></returns>
    private static bool BuildAndroidStudioProject(bool bIsOpenDirectory)
    {
        if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android)
        {
            UnityEditor.EditorUtility.DisplayDialog("打AAR包失败", "请切换到Android平台", "确认");
            return false;
        }

        // 设置成Gradle打包
        EditorUserBuildSettings.androidBuildType = AndroidBuildType.Release;
        EditorUserBuildSettings.buildScriptsOnly = false;
        EditorUserBuildSettings.androidBuildSystem = AndroidBuildSystem.Gradle;
        EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
        // 设置productName,因为如果设置为中文名的话,导出的工程就是中文名,后面生成AAR会失败
        var productName = PlayerSettings.productName;
        PlayerSettings.productName = androidProjectName;

        var scenes = AutoBuilder.GetScenePaths();

        var publisPath = GetPublishPath();
        if (Directory.Exists(publisPath))
            Directory.Delete(publisPath, true);

        // BuildOptions必须设置为AcceptExternalModificationsToPlayer,不然的话会打出不带后缀的文件,不是导出AndroidStudio工程
        var message = BuildPipeline.BuildPlayer(scenes, publisPath, BuildTarget.Android, BuildOptions.AcceptExternalModificationsToPlayer);
        PlayerSettings.productName = productName;
        if (!string.IsNullOrEmpty(message))
        {
            UnityEngine.Debug.LogError("build message: " + message);
            return false;
        }
        else
            UnityEngine.Debug.Log("<color=\"blue\">Build Android Studio Project: Succeed</color>");

        if(bIsOpenDirectory)
            OpenDirectory(publisPath);

        return true;
    }

    /// <summary>
    /// 生成AAR
    /// </summary>
    private static void BuildAAR()
    {
        var projectPath = GetPublishPath() + "/" + androidProjectName;
        if(!Directory.Exists(projectPath))
        {
            UnityEngine.Debug.LogError("Android Studio Project was not found: " + projectPath);
            return;
        }

        if(SetupAndroidStudioProject(projectPath))
        {
            int lastIndex = Application.dataPath.LastIndexOf("/");
            string path = Application.dataPath.Substring(0, lastIndex) + "/BulidAAR";
            File.Copy(path + "/BuildAAR.bat", projectPath + "/BuildAAR.bat", true);
            Process proc = System.Diagnostics.Process.Start(projectPath + "/BuildAAR.bat");
            proc.WaitForExit();
        }
    }

    /// <summary>
    /// 设置Android Studio工程一些参数
    /// </summary>
    /// <param name="projectPath"></param>
    private static bool SetupAndroidStudioProject(string projectPath)
    {
        if (!Directory.Exists(projectPath))
        {
            UnityEngine.Debug.LogError("Android Studio Project was not found: " + projectPath);
            return false;
        }

        if (null == andoridFiles)
            andoridFiles = new Dictionary<string, string>();
        else
            andoridFiles.Clear();

        FindFiles(andoridFiles, projectPath, androidNeedUpdateFile);
        var count = 0;
        foreach (var temp in andoridFiles)
        {
            //UnityEngine.Debug.Log("key: " + temp.Key + "---value: " + temp.Value);
            if (temp.Key.Equals("build.gradle"))
            {
                if(UpdateBuildGradle(temp.Value))
                {
                    ++count;
                    UnityEngine.Debug.Log("修改了" + temp.Key + "文件");
                }
            }
            if(temp.Key.Equals("AndroidManifest.xml"))
            {
                if (UpdateAndroidManifest(temp.Value))
                {
                    ++count;
                    UnityEngine.Debug.Log("修改了" + temp.Key + "文件");
                }
            }
        }

        if(androidNeedUpdateFile.Count != count)
        {
            UnityEngine.Debug.Log("有文件漏改了");
            return false;
        }

        return true;
    }

    /// <summary>
    /// 修改build.gradle
    /// </summary>
    /// <param name="filePath"></param>
    /// <returns></returns>
    private static bool UpdateBuildGradle(string filePath)
    {
        if (!File.Exists(filePath))
            return false;

        try
        {
            StreamReader reader = new StreamReader(filePath);
            var content = reader.ReadToEnd().Trim();
            reader.Close();


            if(content.Contains("com.android.application"))                              // 将Application改成Library
                content = content.Replace("com.android.application", "com.android.library");
            if(!content.Contains("//applicationId") && content.Contains("applicationId"))//去掉applicationId
                content = content.Replace("applicationId", "//applicationId");

            StreamWriter writer = new StreamWriter(new FileStream(filePath, FileMode.Create));
            writer.WriteLine(content);
            writer.Flush();
            writer.Close();
        }
        catch (Exception e)
        {
            UnityEngine.Debug.Log("UpdateAndroidManifest - Failed: " + e.Message);
            return false;
        }

        return true;
    }

    /// <summary>
    /// 修改AndroidManifest.xml
    /// </summary>
    /// <param name="filePath"></param>
    /// <returns></returns>
    private static bool UpdateAndroidManifest(string filePath)
    {
        if (!File.Exists(filePath))
            return false;

        try
        {
            StreamReader reader = new StreamReader(filePath);
            var content = reader.ReadToEnd().Trim();
            reader.Close();

            //去掉 intent-filter 标签
            var firstStr = "<intent-filter>";
            var secondStr = "</intent-filter>";
            var firstIndex = content.IndexOf(firstStr);
            var lastIndex = content.LastIndexOf(secondStr);
            var count = lastIndex - firstIndex + secondStr.Length;
            if(firstIndex > 0 && count > 0)
                content = content.Remove(firstIndex, count);

            StreamWriter writer = new StreamWriter(new FileStream(filePath, FileMode.Create));
            writer.WriteLine(content);
            writer.Flush();
            writer.Close();
        }
        catch(Exception e)
        {
            UnityEngine.Debug.Log("UpdateAndroidManifest - Failed: " + e.Message);
            return false;
        }

        return true;
    }

    private static void FindFiles(Dictionary<string, string> findFiles, string path, List<string> needFindFiles)
    {
        if (!Directory.Exists(path))
            return;

        DirectoryInfo dirInfo = new DirectoryInfo(path);
        var files = dirInfo.GetFileSystemInfos();
        for (int i = 0; i < files.Length; ++i)
        {
            var fileName = files[i].Name;
            if (fileName.Contains("."))//文件
            {
                var fileFullName = files[i].FullName;
                for (int j = 0; j < needFindFiles.Count; ++j)
                {
                    if(fileName.Equals(needFindFiles[j]))
                    {
                        if (findFiles.ContainsKey(fileName))
                            findFiles[fileName] = fileFullName;
                        else
                            findFiles.Add(fileName, fileFullName);
                    } 
                }
            }
            else//文件夹
                FindFiles(findFiles, files[i].FullName, needFindFiles);
        }
    }

    /// <summary>
    /// Android Studio工程输出路径
    /// 使用这个路径(Application.dataPath + "/.."),会失败,说是(Build Path same as Project Path)
    /// </summary>
    /// <returns></returns>
    public static string GetPublishPath()
    {
        int lastIndex = Application.dataPath.LastIndexOf("/");
        string publishPath = Application.dataPath.Substring(0, lastIndex) + "/output";
        //UnityEngine.Debug.LogError("publishPath: " + publishPath);

        return publishPath;
    }

    #region 打开文件夹
    public static void OpenDirectory(string path)
    {
        // 新开线程防止锁死
        Thread newThread = new Thread(new ParameterizedThreadStart(CmdOpenDirectory));
        newThread.Start(path);
    }

    private static void CmdOpenDirectory(object obj)
    {
        Process p = new Process();
        p.StartInfo.FileName = "cmd.exe";
        p.StartInfo.Arguments = "/c start " + obj.ToString();
        //UnityEngine.Debug.Log(p.StartInfo.Arguments);
        p.StartInfo.UseShellExecute = false;
        p.StartInfo.RedirectStandardInput = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.CreateNoWindow = true;
        p.Start();

        p.WaitForExit();
        p.Close();
    }
    #endregion
}

代码中有用到一个批处理文件(BuildAAR.bat),代码也放出来,也很简单,就2句代码。

登录后复制

cd /d %~dp0

start gradle clean assembleRelease

参考了这篇文章 unity打aar包工具,大佬是用Android Studio工程生成AAR的,而且还是用java写的,java窗口怎么生成还要用什么IDE的完全不清楚(没学过),只好用c#写,本来还打算用批处理来处理IO操作的,说尽量简单点,那就没办法了。



注意:

用了一段时间没什么大问题,但因为.gradle的缓存文件在C盘,导致C盘快炸了,只剩下5个g的内存。所以只好删除缓存文件,切换到其他盘区。修改到F盘后,发现用AndroidStudio可以编译出AAR,用批处理打AAR失败,一直在报这个错误Release resolve files of classpath。网上搜了半天都没找到解决的方法,看到一张图用的是Use local gradle distribution,在想是不是这个问题,测试下,发现没问题,标记下,省的以后花很多时间来重复这个问题。

androidstudio unity打包apk unity打包aab_xml_02


免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删

QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空