[Unity]Unity一键生成AAR包

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

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

       有些比较重要的我都写了注释,所以也就懒得解释了。Unity自带的API导出Android Studio Project工程的地方需要注意下,注意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操作的,说尽量简单点,那就没办法了。

猜你喜欢

转载自blog.csdn.net/zp288105109a/article/details/81569079