找回密码
 立即注册
查看: 473|回复: 0

如何高效定位Unity安卓开发中的闪退问题

[复制链接]
发表于 2020-12-29 10:15 | 显示全部楼层 |阅读模式
这是侑虎科技第409篇文章,感谢作者雨松MOMO供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群465082844)
作者主页:http://www.xuanyusong.com
作者也是U Sparkle活动参与者,UWA欢迎更多开发朋友加入U Sparkle开发者计划,这个舞台有你更精彩!
Unity安卓开发遇到闪退基本都会是这个错误“Fatal signal 11 (SIGSEGV)”,意思就是内存引用可能出现错误,下面还有很多“#00 pc 00a3b772c #01 pc 006a4310”, 它就是错误栈但是如果没有带符号表的so是看不懂的。
Unity的源码都是C++写的,为了让应用层使用C#开发,Runtime环境下对外封装了UnityEngine.DLL,这样应用层访问UnityEngine.DLL,由它再访问底层C++部分代码。前面我们讲的Fatal Signal 11 (SIGSEGV)其实就是挂在了Unity的底层代码中。
Unity的底层代码我们是改不了的,为了解决闪退只有一个办法就是找到调用它的地方,绕过底层的闪退。通过我的经验,大部分闪退都是由于应用层传递了错误的数据,引擎内部可能还会对传入的数据进行传递或转换,过程中如果出现故障就挂了。
最简单的办法就是打包的时候勾选Development Build。
勾选Development Build后,当出现闪退以后连上Logcat就能直接看到问题在哪里,由于我们用的是il2cpp打的包,这里直接能看到完整引起闪退的日志。
上述方法也存在一个问题,总不能每次打包都打Debelopment Build版本吧,如果已经是发布出去的版本出现闪退如何定位呢?如图所示,Unity已经将自己内部的符号表公开出来了,找到il2cpp或者Mono下的Symbols下的.so即可。
接着我要在Android SDK中找到“arm-linux-androideabi-addr2line”用来还原这个错误栈。
  1. I/DEBUG(242): backtrace:
  2. I/DEBUG(242):     #00  pc 006d4960  /data/app-lib/com.u.demo-1/libunity.so
  3. I/DEBUG(242):     #01  pc 006d4c0c  /data/app-lib/com.u.demo-1/libunity.so
  4. I/DEBUG(242):     #02  pc 006d4c0c  /data/app-lib/com.u.demo-1/libunity.so
  5. I/DEBUG(242):     #03  pc 006d4c0c  /data/app-lib/com.u.demo-1/libunity.so
  6. I/DEBUG(242):     #04  pc 006d4c0c  /data/app-lib/com.u.demo-1/libunity.so
  7. I/DEBUG(242):     #05  pc 001c5510  /data/app-lib/com.u.demo-1/libunity.so
  8. I/DEBUG(242):     #06  pc 001c595c  /data/app-lib/com.u.demo-1/libunity.so
  9. I/DEBUG(242):     #07  pc 001c4ec0  /data/app-lib/com.u.demo-1/libunity.so
  10. I/DEBUG(242):     #08  pc 0043a05c  /data/app-lib/com.u.demo-1/libunity.so
  11. I/DEBUG(242):     #09  pc 0000d248  /system/lib/libc.so (__thread_entry+72)
  12. I/DEBUG(242):     #10  pc 0000d3e0  /system/lib/libc.so (pthread_create+240)
  13. arm-linux-androideabi-addr2line -f -C -e   /Applications/Unity.app/Content/PlaybackEngines/AndroidPlayer/Variations/mono/Release/Symbols/armeabi-v7a/libunity.sym.so 0043a05c 006d4c0c 006d4c0c
复制代码
如果你的项目是il2cpp,那么每次打包后都需要拿到生成的il2cpp符号表so。这里引用Unity官方的一篇文章:
  1. using UnityEngine;
  2. using System.Collections;
  3. using UnityEditor.Callbacks;
  4. using UnityEditor;
  5. using System.IO;
  6. using System;
  7. public class MyBuildPostprocessor  
  8. {
  9.         [PostProcessBuildAttribute()]
  10.         public static void OnPostprocessBuild(BuildTarget target, string pathToBuiltProject)
  11.         {
  12.                 if (target == BuildTarget.Android)
  13.                             PostProcessAndroidBuild(pathToBuiltProject);
  14.         }
  15.         public static void PostProcessAndroidBuild(string pathToBuiltProject)
  16.         {
  17.                 UnityEditor.ScriptingImplementation backend = UnityEditor.PlayerSettings.GetScriptingBackend(UnityEditor.BuildTargetGroup.Android) as UnityEditor.ScriptingImplementation;
  18.                 if (backend == UnityEditor.ScriptingImplementation.IL2CPP)
  19.                 {
  20.                         CopyAndroidIL2CPPSymbols(pathToBuiltProject, PlayerSettings.Android.targetDevice);
  21.                 }
  22.         }
  23.         public static void CopyAndroidIL2CPPSymbols(string pathToBuiltProject, AndroidTargetDevice targetDevice)
  24.         {
  25.                 string buildName = Path.GetFileNameWithoutExtension(pathToBuiltProject);
  26.                 FileInfo fileInfo = new FileInfo(pathToBuiltProject);
  27.                 string symbolsDir = fileInfo.Directory.Name;
  28.                 symbolsDir = symbolsDir + "/"+buildName+"_IL2CPPSymbols";
  29.                 CreateDir(symbolsDir);
  30.                 switch (PlayerSettings.Android.targetDevice)
  31.                 {
  32.                       case AndroidTargetDevice.FAT:
  33.                         {
  34.                             CopyARMSymbols(symbolsDir);
  35.                             CopyX86Symbols(symbolsDir);
  36.                             break;
  37.                         }
  38.                       case AndroidTargetDevice.ARMv7:
  39.                         {
  40.                             CopyARMSymbols(symbolsDir);
  41.                             break;
  42.                         }
  43.                       case AndroidTargetDevice.x86:
  44.                         {
  45.                             CopyX86Symbols(symbolsDir);
  46.                             break;
  47.                         }
  48.                       default:
  49.                       break;
  50.                 }
  51.         }
  52.         const string libpath = "/../Temp/StagingArea/libs/";
  53.         Const string libFilename = "libil2cpp.so.debug";
  54.         private static void CopyARMSymbols(string symbolsDir)
  55.         {
  56.                 string sourcefileARM = Application.dataPath + libpath + "armeabi-v7a/" + libFilename;
  57.                 CreateDir(symbolsDir + "/armeabi-v7a/");
  58.                 File.Copy(sourcefileARM, symbolsDir + "/armeabi-v7a/libil2cpp.so.debug");
  59.         }
  60.         private static void CopyX86Symbols(string symbolsDir)
  61.         {
  62.                 string sourcefileX86 = Application.dataPath + libpath + "x86/libil2cpp.so.debug";
  63.                 File.Copy(sourcefileX86, symbolsDir + "/x86/libil2cpp.so.debug");
  64.         }
  65.         public static void CreateDir(string path)
  66.         {
  67.                 if (Directory.Exists(path))
  68.                     return;
  69.                 Directory.CreateDirectory(path);
  70.         }
  71. }
复制代码
有了so以后就可以用“arm-linux-androideabi-addr2line”来还原闪退栈了。有时候一些重要的闪退现场只有QA那里才有,大家也不希望他们报上来的BUG就是闪退两个字吧。我们最后希望QA上报BUG的时候就把闪退的栈报上来,这样程序就方便多了,帮他们做个工具吧。
在Android SDK目录下拷贝出“adb.exe  AdbWinApi.dll AdbWinUsbApi.dll fastboot.exe ”这些adb需要的依赖库。
当QA测试出现闪退的时候,双击run.bat即可提取日志(我一般让他们搞个快捷方式放在桌面上)。这里需要注意的是千万不要让他们用360手机助手,因为它会持续占用adb端口,别的手机助手都没这问题,比如豌豆荚、PP助手。
  1. Run.bat
  2. @echo off  
  3. set "year=%date:~0,4%"
  4. set "month=%date:~5,2%"
  5. set "day=%date:~8,2%"
  6. set "hour_ten=%time:~0,1%"
  7. set "hour_one=%time:~1,1%"
  8. set "minute=%time:~3,2%"
  9. set "second=%time:~6,2%"
  10. set adb="%~dp0\adb.exe"
  11. echo %adb%
  12. %adb% logcat -v time -d >  %year%%month%%day%%hour_ten%%hour_one%%minute%%second%.log &
复制代码
如果大家接了闪退汇报的SDK,也可以将so传上去,我用的是Fabirc每次都把il2cpp的so传上去,看闪退也挺方便。
另外,闪退需要解决,但是异常错误也需要解决。前面我们介绍过挂在Unity底层会引起闪退,但是如果挂在我们自己写的代码中那就是异常了,比如常见的空指针数组越界等。
这里提供一个思路,方便我们后续提交Bug:在代码中监听“Application.logMessageReceived”的事件,统计到了异常直接输出显示在屏幕中。这样QA在报BUG的时候可以截个屏,程序看到就方便修改了。
文末,再次感谢雨松MOMO的分享,如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:465082844)。
也欢迎大家来积极参与U Sparkle开发者计划,简称"US",代表你和我,代表UWA和开发者在一起!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-20 08:59 , Processed in 0.090521 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表