前言

基于工作原因,最近一直在做Android Lolipop的ROM,本周在测试时发现了一个问题:重置、刷机后第一次启动,在开机时概率出现system_server进程多次挂掉,断电重启后有机会恢复正常。正常后再开机问题不出现。

分析

开机时system_server进程挂掉,基本是framework/service/目录下的服务出了异常。抓到的打印也符合我们的判断,有两种异常:

  • 第一种
E/AndroidRuntime(  619): *** FATAL EXCEPTION IN SYSTEM PROCESS: Thread-56
E/AndroidRuntime(  619): java.lang.ArrayIndexOutOfBoundsException: length=398; index=398
E/AndroidRuntime(  619):        at java.util.Collections.sort(Collections.java:1888)
E/AndroidRuntime(  619):        at com.android.server.AssetAtlasService.computeBestConfiguration(AssetAtlasService.java:428)
E/AndroidRuntime(  619):        at com.android.server.AssetAtlasService.chooseConfiguration(AssetAtlasService.java:481)
E/AndroidRuntime(  619):        at com.android.server.AssetAtlasService.access$100(AssetAtlasService.java:66)
E/AndroidRuntime(  619):        at com.android.server.AssetAtlasService$Renderer.run(AssetAtlasService.java:226)
E/AndroidRuntime(  619):        at java.lang.Thread.run(Thread.java:818)
E/AndroidRuntime( 2237): FATAL EXCEPTION: main
  • 第二种
E/AndroidRuntime(  617): *** FATAL EXCEPTION IN SYSTEM PROCESS: Thread-56
E/AndroidRuntime(  617): java.util.ConcurrentModificationException
E/AndroidRuntime(  617):        at java.util.AbstractList$FullListIterator.set(AbstractList.java:148)
E/AndroidRuntime(  617):        at java.util.Collections.sort(Collections.java:1888)
E/AndroidRuntime(  617):        at com.android.server.AssetAtlasService.computeBestConfiguration(AssetAtlasService.java:428)
E/AndroidRuntime(  617):        at com.android.server.AssetAtlasService.chooseConfiguration(AssetAtlasService.java:481)
E/AndroidRuntime(  617):        at com.android.server.AssetAtlasService.access$100(AssetAtlasService.java:66)
E/AndroidRuntime(  617):        at com.android.server.AssetAtlasService$Renderer.run(AssetAtlasService.java:226)
E/AndroidRuntime(  617):        at java.lang.Thread.run(Thread.java:818)

根据异常log来看,出问题的点都在AssetAtlasService中,根据说明,此服务是在启动时将一些系统图片资源合并成一个纹理图传给GPU达到硬件加速的效果。
不过它干了什么跟这错误没太大关系,回归正题,我们能看出引起问题的原因应该是多线程竞争。ConcurrentModificationException,是当前线程使用迭代器对集合进行迭代时有另外线程对此集合做了更改引起的。
看下源码。

    private static Configuration computeBestConfiguration(ArrayList<Bitmap> bitmaps, int pixelCount) {......// Don't bother with an extra thread if there's only one processorint cpuCount = Runtime.getRuntime().availableProcessors();if (cpuCount == 1) {new ComputeWorker(MIN_SIZE, MAX_SIZE, STEP, bitmaps, pixelCount, results, null).run();} else {int start = MIN_SIZE;int end = MAX_SIZE - (cpuCount - 1) * STEP;int step = STEP * cpuCount;final CountDownLatch signal = new CountDownLatch(cpuCount);for (int i = 0; i < cpuCount; i++, start += STEP, end += STEP) {ComputeWorker worker = new ComputeWorker(start, end, step,bitmaps, pixelCount, results, signal);new Thread(worker, "Atlas Worker #" + (i + 1)).start();}try {signal.await(10, TimeUnit.SECONDS);} catch (InterruptedException e) {Log.w(LOG_TAG, "Could not complete configuration computation");return null;}}// Maximize the number of packed bitmaps, minimize the texture sizeCollections.sort(results, new Comparator<WorkerResult>() {@Overridepublic int compare(WorkerResult r1, WorkerResult r2) {int delta = r2.count - r1.count;if (delta != 0) return delta;return r1.width * r1.height - r2.width * r2.height;}});......return new Configuration(result.type, result.width, result.height, result.count);}
    /*** A compute worker will try a finite number of variations of the packing* algorithms and save the results in a supplied list.*/private static class ComputeWorker implements Runnable {......@Overridepublic void run() {if (DEBUG_ATLAS) Log.d(LOG_TAG, "Running " + Thread.currentThread().getName());Atlas.Entry entry = new Atlas.Entry();for (Atlas.Type type : Atlas.Type.values()) {for (int width = mStart; width < mEnd; width += mStep) {for (int height = MIN_SIZE; height < MAX_SIZE; height += STEP) {// If the atlas is not big enough, skip itif (width * height <= mThreshold) continue;final int count = packBitmaps(type, width, height, entry);if (count > 0) {mResults.add(new WorkerResult(type, width, height, count));// If we were able to pack everything let's stop here// Increasing the height further won't make things betterif (count == mBitmaps.size()) {break;}}}}}if (mSignal != null) {mSignal.countDown();}}......}

可以看出,代码中根据cpu核心数量启动相应个数的线程,在线程中往results集合里面添加元素。在所有线程执行完毕后对集合进行排序。如果锁等待出错,则直接返回。

  • 题外话:代码中的CountDownLatch提供了一个同步计数器,每次调用countDown计数减一,为0时await不再阻塞等待。用来等待多线程工作全部结束非常好用。是concurrent包提供的并发工具之一。

看来就是这里的多线程代码出现了错误,当系统初次启动时,CPU工作较多,多线程工作没有全部做完,await指定的超时时间10s就已经到时。此时程序继续向下运行开始对集合排序,同时另外线程又可能正好对集合进行add操作,于是出现异常,挂掉。

原因

根据API描述,CountDownLatch的await(time, timeunit)方法会等待参数指定的时间,如果到时计数器仍不为0,则会返回false,反之返回true。所以,代码中并没有对该方法的返回值做判断,不论是等待超时亦或正常完成,都对集合开始排序,因此就导致了开头我们看到的问题现象。

解决

要解决此问题,应该对await方法的返回值做判断,如果为false,则直接返回,不再排序即可。在更改之前,我决定上AOSP看下Google有没有发现这个问题。
首先,找到platform/frameworks/base/这个Git仓库,我们要看的是最新代码,就进入master分支。
然后,打开service/core/java/com/android/server/AssetAtlasService.java文件,看看目前最新版本什么样子。
最后,我们发现:

Xiaohui Chen兄在15年4月已经修复了此问题。方法与我们上面所说一致。那么合进当前代码,编译验证OK,问题解决。

后记

这个问题教导了我们,写代码要认真看API

Android Lolipop AssetAtlasService引起的系统崩溃相关推荐

  1. 手机android系统问题怎么解决方案,4解决Android系统崩溃问题的解决方案

    第1部分:如何抢救Android系统崩溃的数据? 当您遇到Android系统崩溃时,在寻找解决方案以解决问题之前,请确保检索存储在设备上的所有数据和信息.这可能听起来很乏味,但确实是非常重要的一步. ...

  2. android 跳转崩溃_iOS系统突现大量bug! 英雄联盟手游配置需求公布、iOS13.5全设备越狱到来、三星手机系统崩溃...

    1英雄联盟手游配置需求公布近日英雄联盟手游发布消息称,将于6月份在巴西和菲律宾进行限量首测.同时还公布了英雄联盟手游对于设备配置的最低要求,如果是iOS设备的话最低需要 iPhone6 也就是A8处理 ...

  3. Android M及以上版本系统 悬浮窗权限 的解决方案

    Android M及以上版本系统 悬浮窗权限 的解决方案 Android的窗口体系中,WindowManager占有非常重要的地位,平时我们使用悬浮窗会遇到一些权限的问题. 当 Android工程在 ...

  4. 如何设计王者荣耀角色转移服务避免系统崩溃(附服务架构方案)

    如何设计王者荣耀角色转移服务避免系统崩溃(附服务架构方案) 技术背景 为啥会出现这个需求 角色转移事件回放 角色转移服务架构设计 技术背景 为啥会出现这个需求 按照王者荣耀这款游戏的技术总监的王者荣耀 ...

  5. android 6.0 x86 64,安卓x86 6.0 iso下载|android x86 6.0 iso系统下载RC1 版_64位/32位IT猫扑网...

    android x86 6.0 iso系统镜像最新发布,rc1版修复了之前的问题,android-x86 6.0-rc1(marshmallow-x86)这是android-x86 6.0的第一个候选 ...

  6. Android Studio 基础 之 获取系统Calendar 日历日程(可获得当天以后可设定天数范围内的日历日程) (涉及指定日期时间判断是星期几的方法使用)的方法整理

    Android Studio 基础 之 获取系统Calendar 日历日程(可获得当天以后可设定天数范围内的日历日程) (涉及指定日期时间判断是星期几的方法使用)的方法整理 目录 Android St ...

  7. 【问题解决】Android JDK版本不匹配导致崩溃踩坑记录

    [问题解决]Android JDK版本不匹配导致崩溃踩坑记录 部分机型反馈崩溃问题 谷歌回复与解决方案 Android打包脱糖操作 对比与排查 总结 前几天同事遇到一个非常诡异的报错,紧急处理后,趁着 ...

  8. 服务器装系统崩溃,服务器系统崩溃不要慌,因为我用了这招!

    在日常工作中,想必IT工作者们或多或少都碰到过操作系统崩溃的情形,寻找原因的过程中,你是有章可循,还是一头乱麻? 操作系统崩溃具有突发性,既让人猝不及防,又给我们留下一头问号:修复该从何处入手?这时要 ...

  9. Android接入USB音频设备,系统一直发送广播

    Android接入USB音频设备,系统一直发送广播 今天发现有一款USB音频设备接入Android后,系统一直发送广播,导致出现了一些异常情况,下面是预想中的一种方案,在此记录一下. 当USB音频设备 ...

  10. linux 系统崩溃完全没有操作空间的系统修复

    linux 系统崩溃完全没有操作空间的系统修复 1.通过U盘系统启动 2.修复文件系统 https://editor.csdn.net/md/?articleId=106213788 此时硬盘会被挂在 ...

最新文章

  1. CDN全站加速助力企业云上升级
  2. 【乱】乱,乱,乱,android真乱!
  3. Arduino生成ATmega8的运行程序并下载
  4. 第一章--第一节:环境搭建
  5. 概率统计笔记:贝叶斯推断 Bayesian Inference
  6. Day11多态部分-1 【1.1 多态的体现】
  7. 统一过程(UP)定义了初启阶段、精化阶段、构建阶段、移交阶段和产生阶段,每个阶段以达到某个里程碑时结束,其中()的里程碑是生命周期架构。 A.初启阶段 B.精化阶段 C.构建阶段 D.移交阶段
  8. python中location_使用python请求模块时的LocationValueError
  9. 解决 ‘Response‘ object has no attribute ‘body‘
  10. 盘点那些跨界玩到飞起的程序员们!
  11. android 的a标签,Android开发技巧之在a标签或TextView控件中单击链接弹出Activity(自定义动作)...
  12. Joda-Time简单使用
  13. AnyTXT Searcher中文版-比Google Desktop都牛连everything软件作者都拍手称赞的文本搜索工具
  14. 6N137中文说明书 光耦资料 6N137资料
  15. 海信电视 LED55K370 升级固件总结【含固件下载地址】
  16. PCB板设计后期处理流程及工作步骤有哪些?2021-07-29
  17. MySQL 事务四大特性和事务隔离级别
  18. 20162327WJH四则运算第二周总结
  19. 2019.12.11工作遇到问题解答
  20. sublime指定python版本

热门文章

  1. 在centos上更改服务器时区(美国时间、北京时间)
  2. Lumerical官方案例、FDTD时域有限差分法仿真学习(八)——光纤布拉格光栅(Fiber Bragg gratings)
  3. 用于Firefox的Google工具栏Beta 2发布
  4. NLP文本分类--词向量
  5. 如何在IDM中设置代理服务器
  6. 惠普电脑如何安装双系统
  7. 洛谷 CF894A QAQ
  8. 软考中级考试信息系统管理工程师怎么样??
  9. Windows 2008 R2 终端服务器授权安装配置
  10. 蚂蚁金服十年自研分布式中间件,成就世界级新金融科技平台