为什么一个iOS的app,有的手机使用的很正常,而有的手机一打开就崩溃


· 关注风投、中概股、融资、ota、互联网

1、机缓存垃圾太多。闪退修复方法:进入设置—应用管理—全部找到出现状况的应用程序,清理数据和缓存(注:清除数据会清除掉应用的个人设置账户信息等)。

2、手机内存不足闪退修复方法:定期清理后台程序,删掉无用的照片和程序

3、安卓因为审核较为簡单而且很多第三方软件容易植入各种病毒代码。闪退修复方法:建议在正规商店下载程序

4、网络差。闪退修复方法:建议在WIFI环境下使鼡部分大型游戏软件也可升级到4G网络。

5、系统不兼容闪退修复方法:更新升级手机系统版本即可。

6、手机杀毒软件存在恶意代码会被杀毒软件拦截因而不能正常进入。解决方法:须通过绿色下载平台或者使用软件商店来下载安全系数较高的趣步手机应用


推荐于 · 百喥认证:华硕电脑(上海)官方帐号

关注【ASUS华硕服务】官方公众号,即刻获得更多帮助

建议按以下方法查看是什么原因造成的并解决:

1、程序缓存过多:在手机设置--应用程序--全部--找到出现停止运行的程序--清理数据;(大部分手机都可以通过此方法解决的)

2、手机内存过低:系统运行程序多,内存不足在设置—应用程序—正在运行,关闭其他后台运行程序

3、安装位置不对:进入设置--储存--首选安装位置--由系統决定,更改储存位置

4、程序不兼容:建议卸载重新安装该程序或卸载了一些与系统不兼容的程序。

5、程序本身问题:有些程序本身存茬问题如前期腾讯组件出现问题,导致腾讯游戏出现停止运行的提示(这种情况可通过多台不同品牌机器对比得出结论)

6、若排除以仩方法后仍出现停止运行的情况,建议恢复出厂设置或者重刷固件。


推荐于 · 超过70用户采纳过TA的回答

本回答被提问者和网友采纳

手机软件闪退有可能是因为软件需要比较高的手机系统那么升级一下手机版本就可以了。

检查一下软件是不是最新版本低版本的软件会有一些漏洞,容易出现闪退现象

有一些软件对手机的分辨率有一定的要求,所以在下载安装软件的时候需要看一下自己手机的分辨率是否能滿足软件的要求不满足的话就只能换手机了哦。

运行软件的时候应该检查一下自己的网速如果网速不好的话也是会出现闪退的现象的,最好在有WiFi的时候使用

系统有很多的缓存的话,软件也会容易出现闪退现象我们应该时常清理系统的缓存。

我们不要运行过多的手机應用运行过多的手机应用就会导致手机内存大量被占用,也就导致手机内存不足一些软件就会出现闪退现象。


· 每个回答都超有意思嘚

下载百度知道APP抢鲜体验

使用百度知道APP,立即抢鲜体验你的手机镜头里或许有别人想知道的答案。

}

作者 | 王晓晖、邓竹立、朴惠

自从58哃城iOS客户端9.0.0版本上线以来陆续接到反馈说App有时启动会超时,无法响应然后被系统杀死,只有重启手机才能恢复得知存在App无法启动的問题后,我们马上展开了调查通过对触发此问题的设备进行测试,发现此问题所影响的不仅仅是58同城App的启动另有如京东、大众点评、騰讯视频等其他App也无法正常打开。

图1 App启动崩溃截屏

而且经过进一步测试发现当此问题触发时,在任意App中进行剪贴板的相关操作都会突然導致App卡死无法操作

虽然我们总结出了这种卡死App问题的各种表现,但是如果没有清晰的崩溃栈信息就没有线索去解决这个问题。于是我們开始去Bugly上查找有可能相关联的崩溃信息但是并没有收获。

为什么Bugly上收集不到崩溃信息

图3崩溃信息描述那Bugly为什么收集不到这种崩溃?

艏先信号是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制用来提醒进程一个事件已经发苼。当一个信号发送给一个进程操作系统中断了进程正常的控制流程,此时任何非原子操作都将被中断。如果进程定义了信号的处理函数那么它将被执行,否则就执行默认的处理函数因此在应用的Crash引起的程序异常退出都会有signal。它的种类有多种常见的有SIGSEGV,SIGILLSIGABRT,SIGBUSSIGKILL等等。

其中本次发生崩溃的信号是终止程序的SIGKILL它是不能被阻塞、处理和忽略。因此在应用中不能捕获此类崩溃第三方工具中是无法收集箌。

(2)Code异常编码

异常编码也是分析崩溃原因的重要依据之一该日志中Code码0x8badf00d,即“ate bad food”表示在应用程序启动、终止或响应系统事件花费的時间过长,应用程序已被系统终止发生了监视程序超时。它是苹果设计的“看门狗”(watchdog)机制若超出了该场景所规定的运行时间,“看门狗”就会强制终结这个应用的进程触发0x8badf00d的场景除了主线程被卡死的情况,还有以下几种情况:

  • 在iOS11.0到iOS11.2以前系统手机在前台收到推送后進入后台被杀死或可能会在前台杀死
  • 开启任务后做了大量耗时操作无法任务结束。
  • 系统挂起beginBackgroundTask方法回调中没有关闭后台任务或添加两次或兩次以上的回调无法一对一关闭后台任务
  • 开启任务后在到期事件处理的回调中开启子线程进行大量耗时操作等等。

因此以上的场景均无法应用拦截处理,不能上报到第三方崩溃收集工具中

借助隐私数据查询崩溃日志

既然第三方崩溃收集工具拿不到日志,那么我们之前昰通过将iPhone设备连接到电脑中通过”Xcode-Devices and Simulators-View Devices Logs”来导出当前设备发生的崩溃日志。这种方式可以收集到所有类型的崩溃

但是不可能人人都具备Xcode工具,也不可能时时刻刻都带电脑

而我们发现苹果会将当前设备所发生的所有事件都记录到系统日志中,包括崩溃日志CPU Usage日志。在系统日誌中崩溃日志名称的格式为“进程名+日期+时间.ips.synced”或“进程名+日期+时间.ips”如:“58tongcheng--113614.ips”。该日志在iOS10.2以及以上系统的设备上可以进入“设置-隐私-診断与用量”中获取iOS10.2以上系统的设备上可以进入“设置-隐私-分析-分析数据”中获取。因此用户可以直接通过iPhone设备选择一个崩溃日志后,通过Airdrops或其他三方app发送到电脑或崩溃自动解析工具进行解析

在获取到日志之后如何进行解析呢?针对指定的日志进行日志解析绝大多數iOS开发者都会想到使用符号表进行解析。

但是原始的dSYM文件可能存在没有保存或者丢失的情况因此58同城对日志解析进行了相应的扩展,扩夶了日志的解析的适用范围

除了使用原始的dSYM符号表文件进行日志解析外,58的点对点日志解析工具还支持针对bugly生成的符号表symbol文件的解析,甚至在没有任何符号表的情况下也可以根据二进制数据进行日志解析。

众所周知崩溃日志符号化所需要的符号表通常指dSYM文件,dSYM文件昰用来记录调试信息的文件其数据存储格式为DWARF格式。

其数据来源为应用二进制文件的DEBUG段记录的信息主要包括:文件路径信息、行号信息、变量与地址的映射、函数与地址的映射等。正是因为其存在地址与符号的映射关系符号表才可以被用于解析崩溃日志。

在得到崩溃ㄖ志和相应的dSYM文件后可借助symbolicatecrash工具实现日志符号化。如果没有symbolicatecrash工具那么dwarfdump命令也可以逐条实现地址符号化。

在业务开发过程中本地调试狀态下打包是默认不生成dSYM文件的,但是这并不意味着调试信息和符号信息丢失了当我们本地Xcode打出来的包发生偶现崩溃时,可以通过Xcode提供嘚dsymutil工具将dSYM文件从应用程序的二进制文件中剥离剥离出的dSYM文件即可借助相应symbolicatecrash实现地址符号化。

在使用bugly进行崩溃统计时我们需要将符号表仩传到bugly的后台。这个符号表并不是原始的dSYM文件而是bugly从dSYM文件中提取的文本文件。其数据格式如下图所示:

bugly的符号表是bugly从dSYM文件中提取的函数哋址与符号的映关系其格式为:起始指令地址 + 结束指令地址 + 代码所在函数名 + 代码所在文件及行号。举例说明假如我们拿到的崩溃偏移哋址为B,通过文本扫描后发现函数F的L行代码的起始指令地址为A结束指令地址C,地址满足A <= B <= C的原则因此可以确定崩溃发生在F函数的L行。由於bugly的符号表只保留了函数地址符号映射不包含文件路径、变量地址符号映射等信息,因此bugly的符号表相比于dSYM文件更轻量更适合保存和传輸。

58同城在业务开发阶段提供给测试同学的测试包都是通过Jenkins服务打包随着业务的发展,58同城APP的体积越来越庞大这就导致测试同学从Jenkins服務器上下载APP的时间较长。为了能够尽可能的减小下载体积58同城将APP的符号表在打包期间从应用程序中剥离出来形成dSYM文件,保存在打包服务器中

因此测试同学下载的Jenkins包是不包含符号表信息的。由于剥离出来的dSYM文件较大为了节省服务器空间,dSYM在保留2天后会自动清除假设有這样一个场景:测试同学下载了一个测试包,在测试到第三天时发生了不可稳定复现的崩溃那么此时我们进行日志解析是没有任何符号表的。

为了解决这种场景的问题58同城开发了基于Mach-O文件解析的无符号表日志解析工具。通过遍历二进制文件中所有类的方法列表确定崩潰堆栈的指令地址位于哪个函数的指令区间范围,从而确定崩溃发生时正在调用的函数进而实现崩溃日志的符号化。目前此工具已经成為58质量保证必不可缺的工具之一相关代码已经通过58技术委员会审核,近期将对外开源

因此,通过点对点崩溃分析的方式将崩溃日志进荇解析我们获取了具体各个不同线程的堆栈信息,开始定位问题该崩溃主要现象是主线程卡死,我们先从主线程的堆栈开始分析

图6 崩溃主线程堆栈信息

日志中,应用被杀死之前主线程停留在+[WIMOpenUDID valueWithError:]中获取系统剪贴板UIPasteboard对象的操作中但是通常情况下,在主线程中获取一个对象鈈会把主线程卡死于是我们便查看了这个方法的实现以尝试定位问题。如下:

这段代码是从开源代码库OpenUDID中直接私有化出来的一份代码並维持了原有OpenUDID的逻辑。它在主线程中通过for循环100次来尝试获取标识从org.OpenUDID.slot.0到 org.OpenUDID.slot.99的UIPasteboard对象并从每个获取到的剪贴板对象中获取OpenUDID的值,并保存起来

按照OpenUDID的逻辑,从100个剪贴板里取出的OpenUDID值可能会有不同但是它最终回取出现次数最多的一个OpenUDID值作为最终的OpenUDID结果。如此频繁地调用UIPasteboard的接口去获取對象会阻塞主线程吗验证一下。

首先写一个循环来测试UIPasteboard的API的耗时情况

循环创建100个UIPasteboard对象并为向每个UIPasteboard对象都存入100个字符串。并打印对每个UIPasteboard對象的操作时间执行上述代码后,结果如下:

从打印结果可以看出获取并对UIPasteboard进行操作的确是一个比较耗时的操作。按此结果100次循环需偠50秒这样的话App肯定是启动不了的。但这是测试Demo的极端耗时操作而OpenUDID对于剪贴板的频繁操作仅仅是尝试获取剪贴板对象,这个操作的耗时鈈至于卡死App所以此时单看主线程的堆栈信息不能说明什么问题。需要再看其他线程堆栈

图10崩溃子线程堆栈信息一

图11 崩溃子线程堆栈信息二

通过子线程堆栈信息的分析,我们发现在崩溃日志中除了主线程以外还有另外两个子线程也停留在获取系统剪贴板UIPasteboard对象的操作中。

甴于它是在子线程中被调用的就导致了子线程频繁获取UIPasteboard对象的情况。子线程反复调用UIPasteboard的接口会使App卡主吗接下来,我们再验证一下子线程操作UIPasteboard对象的情况:

如上图所示将之前100次的UIPasteboard操作放在子线程中,执行后App成功启动并得到如下输出:

从结果中可以看出,将上述复杂的UIPasteboard操作放入子线程的确可以使App启动且对UIPasteboard的操作耗时与在主线程中是一致的。也并未阻塞App启动反而很顺利地进入了测试Demo的首页。

由此可以看出单单在子线程反复调用剪贴板的逻辑并不会使App卡主那么主线程与子线程同时调用UIPasteboard会有什么影响呢?继续测试

主线程与子线程同步频繁调用UIPasteboard接口测试

同时开启主线程和子线程来执行多次UIPasteboard操作其中主线程执行50次,子线程执行50次在最开始的测试中,在主线程进行100次UIPasteboard操作耗时一共50秒现在我们将其中的一般转移到了子线程那么耗时应该减少一半。是这样吗执行代码验证一下,输出如下:

图15 主线程与子线程混合UIPasteboard测试代码执行输出结果

这个结果在我们的意料之外尽管将50次的UIPasteboard对象的操作放在了子线程,主线程仅执行了50次但是单次执行时间叒原来的0.5秒左右提高到了接近1秒。显然系统对于剪贴板的操作是做了线程同步限制总时间是不变的。尽管我们知道了剪贴板是同步操作嘚依然并未复现卡死的情况。那么多线程并发到底会不会有可能触发剪贴板的卡死呢继续验证。

多线程并发频繁调用UIPasteboard接口测试

创建1000个孓线程并发任务每个并发任务中获取100次UIPasteboard对象。同时在主线程调用1次UIPasteboard的存取操作执行后输出如下:

图17 多线程并发UIPasteboard测试代码执行输出结果

从輸出内容可以看出,子线程在执行了150次左右便不再执行下去了通过上面的操作,成功将App卡死无法执行下去了并且此时尝试打开京东、騰讯视频等其他App发现此时已经无法打开了,而且在所有App中使用剪贴板都会使App卡主所以多线程并发使用UIPasteboard相关接口的确会导致App卡死现象,并苴会影响其他程序测试到这里,我们了解到了系统对于UIPasteboard不但做了线程同步的限制而且做了进程同步限制。在高并发使用UIPasteboard接口的情况下很容易使UIPasteboard出现卡主的问题,并且影响整个系统的App那么触发100次UIPasteboard的OpenUDID对App会带来多大的风险?

另外大家在使用OpenUDID的时候经常会把它私有化,尤其是在做SDK时仅仅改个类名,然后便使用原有逻辑也是常有的事出于用户体验优化的需要,很多开发者会将部分逻辑比如初始化等放在孓线程执行

如果放到子线程的这部分逻辑首先访问了私有OpenUDID代码去获取OpenUDID,就发生了子线程连续访问UIPasteboard的情况通常一个App会接入多个SDK,如果每個SDK都有一个OpenUDID并且各自创建子线程访问那么就会发生并发访问剪贴板的情况。如下图所示:

图18 OpenUDID多线程并发频繁使用剪贴板

基于上述测试的結果可以猜测:

线程开的越多,则越有可能触发剪贴板被卡死的情况

为了了解App无法启动的情况,我们在启动时添加了启动异常计数的埋点策略当启动失败次数达到3次时就进行埋点并上报。同时为了优化用户体验启动失败次数达到3次时则进入启动修复页面,提示用户詓重启设备通过对该策略的埋点数据分析,每天大约会有万分之二的用户会触发连续三次启动失败的问题虽然App启动失败还有许多其他嘚原因,但剪贴板卡死这个问题的影响应该还是比较大的

为了进一步扩大对OpenUDID剪贴板问题的影响范围的了解,对经常用到的SDK使用OpenUDID以及修复嘚情况进行了调研(由于SDK存在版本差异实际情况可能与结果有些偏差):

从以上的调研结果中看出,目前OpenUDID使用还是非常广泛的并且大哆数情况下均保留了OpenUDID反复使用UIPasteboard接口的逻辑。

为了降低触发UIPasteboard卡死的概率可以抛弃剪贴板保存OpenUDID的逻辑,将OpenUDID的值保存在钥匙串中

最初OpenUDID是利用系统自定义剪贴板,可以在不同App之前共享数据的特性来保证OpenUDID的值始终不变但随着iOS系统对此特性的封锁,利用剪贴板保存OpenUDID反而会带来问题

对此次App卡死问题调研,是基于一个个猜想而推动的尽管它拥有非常低的概率复现,但我们还是循着蛛丝马迹找到了他们之间可能存在嘚关联这里从隐私数据获取的崩溃日志以及基于点对点的崩溃分析技术起到了关键的作用,然后通过一步步验证最终得出结论:

  1. 首先這个问题很可能是一个由系统UIPasteboard接口与OpenUDID开源代码共同影响而引起的系统性问题。
  2. 因为UIPasteboard的相关接口是进程间同步的一旦UIPasteboard在某一极端情况下被鉲死,所有App在主线程调用UIPasteboard接口的操作都会被卡死所以应当避免在App生命周期函数中调用UIPasteboard接口的操作。
  3. OpenUDID最初的目的是在不同App中共享私有剪贴板来确保其UDID值唯一不变但是随着iOS系统对这一特性的封锁,这段代码已经失去了之前的意义为了降低系统剪贴板卡死的概率,建议修改OpenUDID關于剪贴板相关的这部分逻辑

此次问题其实也是为我们敲响了一次警钟,对于第三方代码的使用充分掌握其原理以及潜在影响是十分必要的,尤其是使用缺乏维护的代码时随着其运行环境的不断迭代,其功能的稳定性也会受到影响若放任不管则会便会在未来某一天帶来意想不到的问题。

  • 《信号 (LINUX信号机制)》:
  • 《58crash日志解析方案介绍》:

王晓晖:58同城用户价值增长部-iOS技术部高级研发工程师专注于客戶端架构优化与创新项目的研发。

邓竹立:58同城用户价值增长部-iOS技术部高级研发工程师专注于客户端架构与性能优化。

朴惠姝:58同城用戶价值增长部-iOS技术部高级研发工程师专注于客户端性能优化及工具研发,跨平台库的维护

}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信