网上有很多接入bugly的教程 但是没有教unity接入bugly符号表的教程

什么是符号表?

符号表是内存地址与函数名、文件名、行号的映射表。符号表元素如下所示:

为什么要配置符号表?

为了能快速并准确地定位用户APP发生Crash的代码位置,Bugly使用符号表对APP发生Crash的程序堆栈进行解析还原

 

第一步:

先去Bugly下载最新版的SDK 附上连接 BuglySDK下载

因为我们要接入Unity 所以选择下载Unity的 不要下错了 

unity显示表情的聊天用手摸组件_unity显示表情的聊天用手摸组件

       

 

下载后解压出来是这个样子 这里面其他东西目前不需要 只需要将 bugly_unity.unitypackage 拖进Unity

unity显示表情的聊天用手摸组件_unity显示表情的聊天用手摸组件_02

unity显示表情的聊天用手摸组件_Android_03

第二步:

去官网添加一个Bugly项目 右上角点我的头像 然后选择我的产品 选择新建产品

unity显示表情的聊天用手摸组件_System_04

unity显示表情的聊天用手摸组件_unity显示表情的聊天用手摸组件_05

 

unity显示表情的聊天用手摸组件_System_06

填上信息 这里要注意一下 android跟ios 是分开的 根据自己的需求创建 然后点击保存

保存后继续点我的头像----我的产品----这时就能看到刚才我们创建的项目了

unity显示表情的聊天用手摸组件_符号表_07

鼠标放到这个项目上 右侧操作下边会出现一个 <设置> 按钮 点击设置

unity显示表情的聊天用手摸组件_System_08

这两个值要存下来 待会要用

第三步:

回到unity 创建脚本在第一个场景创建个obj挂上脚本 然后开始写代码

unity显示表情的聊天用手摸组件_符号表_09

using UnityEngine;

public class BuglySDKMgr : MonoBehaviour
{
   private void Awake(){
       // 开启SDK的日志打印,发布版本请务必关闭
       BuglyAgent.ConfigDebugMode (true);
       // 注册日志回调,替换使用 'Application.RegisterLogCallback(Application.LogCallback)'注册日志回调的方式
       // BuglyAgent.RegisterLogCallback (CallbackDelegate.Instance.OnApplicationLogCallbackHandler);

#if UNITY_IPHONE || UNITY_IOS
        BuglyAgent.InitWithAppId ("这里我没有创建IOS的产品 你们可以自己创建然后把ID填进去");
#elif UNITY_ANDROID
        BuglyAgent.InitWithAppId ("9f87a805b9");
#endif
       // 如果你确认已在对应的iOS工程或Android工程中初始化SDK,那么在脚本中只需启动C#异常捕获上报功能即可
       BuglyAgent.EnableExceptionHandler ();
   }
}

到这里Bugly算是接入完成了 这样unity有错误 在bugly后台就可以看到了

第四步:

接入符号表

下载符号表工具  连接: 符号表工具下载

然后解压放在unity项目Assets同级目录里 一会做自动上传符号表

unity显示表情的聊天用手摸组件_System_10

unity显示表情的聊天用手摸组件_unity显示表情的聊天用手摸组件_11

打开settings填写好ID和key

第五步:

因为符号表需要so文件 编写获取复制so文件脚本

unity显示表情的聊天用手摸组件_Android_12

using UnityEngine;
using UnityEditor.Callbacks;
using UnityEditor;
using System.IO;
using System;

public class MyBuildPostprocessor
{
    [PostProcessBuildAttribute()]
    public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject){
        if (target == BuildTarget.Android)
            PostProcessAndroidBuild(pathToBuiltProject);
    }

    public static void PostProcessAndroidBuild(string pathToBuiltProject){
        UnityEditor.ScriptingImplementation backend =
            UnityEditor.PlayerSettings.GetScriptingBackend(UnityEditor.BuildTargetGroup
                .Android); //as UnityEditor.ScriptingImplementation;

        if (backend == UnityEditor.ScriptingImplementation.IL2CPP) {
            CopyAndroidIL2CPPSymbols(pathToBuiltProject, PlayerSettings.Android.targetArchitectures);
        }
    }

    public static void CopyAndroidIL2CPPSymbols(string pathToBuiltProject, AndroidArchitecture targetDevice){
        string buildName = Path.GetFileNameWithoutExtension(pathToBuiltProject);
        FileInfo fileInfo = new FileInfo(pathToBuiltProject);
        string symbolsDir = fileInfo.Directory.Name;
        symbolsDir = symbolsDir + "/" + buildName + "_IL2CPPSymbols";

        CreateDir(symbolsDir);

        switch (PlayerSettings.Android.targetArchitectures) {
            case AndroidArchitecture.All:
            {
                CopyARMSymbols(symbolsDir);
                CopyX86Symbols(symbolsDir);
                CopyARM64ymbols(symbolsDir);
                break;
            }
            case AndroidArchitecture.ARMv7:
            {
                CopyARMSymbols(symbolsDir);
                break;
            }
            case AndroidArchitecture.X86:
            {
                CopyX86Symbols(symbolsDir);
                break;
            }

            default:
                break;
        }
    }


    const string libpath = "/../Temp/StagingArea/libs/";
    const string libFilename = "libil2cpp.so.debug";

    private static void CopyARMSymbols(string symbolsDir){
        string sourcefileARM = Path.GetFullPath(Application.dataPath + libpath + "armeabi-v7a/" + libFilename);
        CreateDir(symbolsDir + "/armeabi-v7a/");
        try {
            File.Copy(sourcefileARM, symbolsDir + "/armeabi-v7a/libil2cpp.so.debug");
            Debug.Log("sourcefileARM: " + sourcefileARM);
        }
        catch (Exception e) {
            Debug.LogError("CopyARMSymbolsError: " + e.Message);
        }
    }

    private static void CopyX86Symbols(string symbolsDir){
        string sourcefileX86 = Path.GetFullPath(Application.dataPath + libpath + "x86/" + libFilename);
        try {
            File.Copy(sourcefileX86, symbolsDir + "/x86/libil2cpp.so.debug");
            Debug.Log("sourcefileX86: " + sourcefileX86);
        }
        catch (Exception e) {
            Debug.LogError("CopyX86Symbols: " + e.Message);
        }
    }

    private static void CopyARM64ymbols(string symbolsDir){
        string sourcefileX86 = Path.GetFullPath(Application.dataPath + libpath + "arm64-v8a/" + libFilename);
        try {
            File.Copy(sourcefileX86, symbolsDir + "/x86/libil2cpp.so.debug");
            Debug.Log("sourcefileX86: " + sourcefileX86);
        }
        catch (Exception e) {
            Debug.LogError("CopyARM64ymbols: " + e.Message);
        }
    }

    public static void CreateDir(string path){
        if (Directory.Exists(path))
            return;

        Directory.CreateDirectory(path);
    }
}

这样打包的时候就会将so文件复制出来 接下来写上传so文件脚本

unity显示表情的聊天用手摸组件_符号表_13

  

using System.Diagnostics;
using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;
using Debug = UnityEngine.Debug;

public class UpLoadBuglySo
{
    private static string[] soFolders = {"arm64-v8a", "armeabi-v7a", "x86"};

    private static string soCmdTemplate =
        "java -jar buglySymbolAndroid.jar -i #SOPATH#/libil2cpp.so.debug -u -id #ID# -key #KEY# -package #PACKAGE# -version #VERSION#";

    
    /// <summary>
    /// 打完包后调用此方法 自动上传符号表文件
    /// </summary>
    [MenuItem("Tool/自动上传符号表")]
    public static void UploadBuglyso(){
        DeleteSo();
        CopyBuglySo();
        if (EditCommand(out string arg)) {
            Debug.Log("EditCommand: " + arg);
            RunCmd(arg, BuglyToolPath());
        }
    }


    private static void CopyBuglySo(){
        FileTool.CopyFolder(DeBugSOPath(), BuglyToolPath());
        FileTool.OpenFolder(BuglyToolPath());
    }


    private static void DeleteSo(){
        foreach (var folder in soFolders) {
            FileTool.DeleteFolder(BuglyToolPath() + "/" + folder);
        }
    }

    private static bool EditCommand(out string arg){
        bool exists = false;
        StringBuilder sb = new StringBuilder();
        foreach (var folder in soFolders) {
            string soPath = BuglyToolPath() + "/" + folder;
            if (FileTool.CheckFolder(soPath)) {
                exists = true;
                sb.Append(soCmdTemplate).Append(" & ");
                sb.Replace("#SOPATH#", soPath);
                sb.Replace("#ID#", "9f87a805b9");  //这里注意一下要改成自己的id
                sb.Replace("#KEY#", "b649f9ed-94f3-4498-954c-460413075256"); //自己的key
                sb.Replace("#PACKAGE#", Application.identifier);
                sb.Replace("#VERSION#", Application.version);
            }
        }

        sb.Append("exit").Replace("/", @"\");
        arg = sb.ToString();
        return exists;
    }
    
    public static string DeBugSOPath(){
        return Path.GetFullPath(Application.dataPath + "/../Temp/StagingArea/symbols");
    }

    public static string BuglyToolPath(){
        return Path.GetFullPath(Application.dataPath + "/../buglytools");
    }
    public static void RunCmd(string arg, string workingDirectory,string exe ="cmd.exe"){
            
        ProcessStartInfo info = new ProcessStartInfo(exe);
        info.Arguments = "/c " + arg;
        info.WorkingDirectory = workingDirectory;
        info.CreateNoWindow = false;
        info.ErrorDialog = true; 
        info.UseShellExecute = true;

        if (info.UseShellExecute) {
            info.RedirectStandardOutput = false;
            info.RedirectStandardError = false;
            info.RedirectStandardInput = false;
        }
        else {
            info.RedirectStandardOutput = true;
            info.RedirectStandardError = true;
            info.RedirectStandardInput = true;
            info.StandardOutputEncoding = System.Text.UTF8Encoding.UTF8;
            info.StandardErrorEncoding = System.Text.UTF8Encoding.UTF8;
        }
        Debug.Log("RunCmd: "+info.Arguments);

        Process process = Process.Start(info);

        if (!info.UseShellExecute) {
            Debug.Log(process.StandardOutput);
            Debug.Log(process.StandardError);
        }

        process.WaitForExit();
        process.Close();
    }
}

这是一个工具类 用于复制

using System.Collections.Generic;
using System.IO;
using UnityEditor;
using Debug = UnityEngine.Debug;


public class FileTool
{
    public static bool CheckFolder(string path){
        if (Directory.Exists(path)) {
            return true;
        }
        UnityEditor.EditorUtility.DisplayDialog("Error", "Path does not exist \n\t" + path, "确认");
        return false;
    }
    public static void OpenFolder(string path){
        if (CheckFolder(path)) {
            System.Diagnostics.Process.Start( path);
        }
        
    }
    
    public static void CopyFolder(Dictionary<string, string> copyDic){
        foreach (KeyValuePair<string, string> path in copyDic) {

            if (CheckFolder(path.Key)) {
                
                CopyDir(path.Key, path.Value);
                Debug.Log("Copy Success : \n\tFrom:" + path.Key + " \n\tTo:" + path.Value);
            }
        }
        EditorUtility.ClearProgressBar();
    }

    public static void CopyFolder(string fromPath, string toPath){
        CopyDir(fromPath, toPath);
        Debug.Log("Copy Success : \n\tFrom:" + fromPath + " \n\tTo:" + toPath);
        EditorUtility.ClearProgressBar();
    }
       
    public static void CreateFolder(string path){
        if (Directory.Exists(path)) {
            Directory.Delete(path, true);
        }
        Directory.CreateDirectory(path);
    }

    public static void DeleteFolder(string path){
        if (Directory.Exists(path)) {
            Directory.Delete(path, true);
        }
    }

    private static void CopyDir(string origin, string target){
#if UNITY_IOS
      
        if (!origin.EndsWith("/"))
        {
            origin += "/";
        }

        if (!target.EndsWith("/"))
        {
            target += "/";
        }
#else
          if (!origin.EndsWith("\\")) {
            origin += "\\";
        }

        if (!target.EndsWith("\\")) {
            target += "\\";
        }

#endif
        if (!Directory.Exists(target)) {
            Directory.CreateDirectory(target);
        }
    
        DirectoryInfo info = new DirectoryInfo(origin);
        FileInfo[] fileList = info.GetFiles();
        DirectoryInfo[] dirList = info.GetDirectories();
        float index = 0;
        foreach (FileInfo fi in fileList) {
            

            if (fi.Extension == ".zip" || fi.Extension == ".meta"|| fi.Extension == ".rar") {
                Debug.Log("dont copy :"+fi.FullName);
                continue;
            }
            float progress = (index / (float)fileList.Length); 
            EditorUtility.DisplayProgressBar("Copy ", "Copying: "+Path.GetFileName(fi.FullName),progress);
            File.Copy(fi.FullName, target + fi.Name, true);
            index++;
        }

        foreach (DirectoryInfo di in dirList) {
            if (di.FullName.Contains(".svn")) {
                Debug.Log("Continue SVN "+di.FullName);
                continue;
            }

            CopyDir(di.FullName, target + "\\" + di.Name);
        }
    }
    
   
    
}

第六步:

编写一个自动出包工具 出完包自动调用上传符号表工具 一些列完成

unity显示表情的聊天用手摸组件_unity显示表情的聊天用手摸组件_14

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Build.Reporting;
using UnityEngine;

public class BuildTool 
{
    const string LOCATION_PATH_ANDROID = "./android.apk";
   
    [MenuItem("Tool/打Android包")]
    public static void ProjectBuild()
    {
        ProjectBuildExecute(BuildTarget.Android);
    }

    public static void ProjectBuildExecute(BuildTarget target)
    {

        //Switch Platform
        SwitchPlatform(target);


   
        List<string> scenes = new List<string>();
        foreach (UnityEditor.EditorBuildSettingsScene scene in EditorBuildSettings.scenes)
        {
            if (scene.enabled)
            {
                if (System.IO.File.Exists(scene.path))
                {
                    Debug.Log("Add Scene (" + scene.path + ")");
                    scenes.Add(scene.path);
                }
            }
        }

       
        BuildReport report = BuildPipeline.BuildPlayer(scenes.ToArray(), LOCATION_PATH_ANDROID, target, BuildOptions.None);
        if (report.summary.result != BuildResult.Succeeded)
        {
            Debug.LogError("打包失败。(" + report.summary.ToString() + ")");
        }
        
    }
    
    private static void SwitchPlatform(BuildTarget target)
    {

        if (EditorUserBuildSettings.activeBuildTarget != target)
        {
            if (target == BuildTarget.iOS)
            {
                EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.iOS, BuildTarget.iOS);
            }
            if (target == BuildTarget.Android)
            {
                EditorUserBuildSettings.SwitchActiveBuildTarget(BuildTargetGroup.Android, BuildTarget.Android);
            }
        }

    }
}

unity显示表情的聊天用手摸组件_Android_15

这里要注意 要改成IL2CPP模式 不然复制不到so文件

unity显示表情的聊天用手摸组件_System_16

先打包 然后在执行自动上传符号表

全部完成之后就可以在后台看见符号表啦

unity显示表情的聊天用手摸组件_System_17