前言

ANR(Application Not Response)应用程序未响应,当主线程被阻塞时,就会弹出如下弹窗

要么关闭当前app,要么就等待,其实这个时候没有挽救的措施,选择等待最终的结果也是ANR,最终都需要杀掉应用进程,我们看下日志,原因是Input dispatching timed out,点击事件处理超时导致ANR。

2022-08-27 16:11:53.168 2057-2080/system_process E/ActivityManager: ANR in com.lay.image_process (com.lay.image_process/.MainActivity)
    PID: 31848
    Reason: Input dispatching timed out (Waiting to send non-key event because the touched window has not finished processing certain input events that were delivered to it over 500.0ms ago.  Wait queue length: 6.  Wait queue head age: 5525.3ms.)

其实相对于其他的错误,ANR比较棘手在于,没有崩溃日志,定位问题比较困难,而且ANR是必须要解决的问题,这个用户体验极差,因此本章的核心在于攻坚ANR问题。

1 ANR原因总结

从上面的日志中,我们看到造成ANR的原因是Input dispatching timed out,那么除此之外,还有什么其他的错误。

1.1 KeyDispatchTimeout

input事件在5s之内没有处理,产生ANR;这种异常是比较常见的问题,常发生在点击事件处理中,logcat的关键字就是Input dispatching timed out

这里需要说明一点,Input事件导致ANR跟下面几种不同的是,看下面的代码,点击按钮5s后,才弹出toast,这种情况下会发生ANR吗?

btnSend.setOnClickListener {
    Thread.sleep(5 * 1000)
    ToastUtils.setText(this)
}

我们可以在私下测试一下,其实是不会的,如果用户后续没有继续输入事件,那么就不会产生ANR

1.2 BroadCastTimeout

class MyBroadCast : BroadcastReceiver(){
    override fun onReceive(context: Context?, intent: Intent?) {
        //TODO 接收广播
    }
}

我们在使用广播接收器接收广播时,需要重写BroadcastReceiver的onReceive方法,当前方法是在主线程中,如果在10s内没有处理弯沉,就会ANR。

因此,在onReceive方法中不能做耗时操作,如果需要则需要创建新的线程。logcat关键字是 Timeout of broadcast BroadcastRecord

1.3 ServiceTimeout

class MyService : Service(){

    override fun onCreate() {
        super.onCreate()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onBind(intent: Intent?): IBinder? {
        TODO("Not yet implemented")
    }

}

同样,在Service中依然不能做耗时操作,如onCreate、onStartCommand、onBind方法中如果超过20s没有处理完成,就会ANR。

所以如果需要在服务中执行耗时操作,建议使用IntentService,logcat关键字是 Timeout executing service

1.4 ContentProviderTimeout

四大组件最后一个组件,如果ContentProvider在10内没有处理就会导致ANR,这个组件使用很少,暂时先不分析

综上所述,如果出现ANR,主要原因就是在主线程执行了耗时操作,导致UI线程被阻塞发生ANR;那么在我们的实际项目中,有哪些操作,可能会导致ANR呢?

1. 主线程进行频繁的IO操作

不知道我们还有多少在使用SP存储的,其实它底层就是通过IO读写操作文件,如果频繁地在主线程进行SP读写可能会造成卡顿或者ANR,之前就有过线上的ANR事故,建议大家都是用MMKV,读写速度秒杀SP;

除此之外,主线程进行网络操作也会导致ANR

2. 多线程争夺资源导致死锁3. CPU资源耗尽

等等......

2 ANR问题解决

2.1 线下问题解决

如果在我们实际的开发过程中,如果出现ANR,那么很简单,打开logcat窗口就可以查看,还有一种方式,就是查看trace日志,路径为 /data/anr/xxx

 打开trace日志,通过这一部分就能猜到具体原因了,就是因为在主线程中,响应点击事件时,线程进入休眠阻塞

线下出问题对于我们来说永远都是最简单的,难的就是在线上出了问题,用户隔你十万八千里,该如何处理?

2.2 线上问题解决

2.2.1 Bugly

可能很多小伙伴的项目中都集成了bugly,确实bugly是很不错的线上监控组件,像Crash、ANR都能够检测到,但是很多时候,日志是不全的,堆栈信息不全就没法定位问题,bugly可以作为兜底方案,具体的监控方案,我们可以自己实现。

2.2.2 FileObserver

对于线上监控,往往有两种方式,我这边先讲解第一种,通过FileObserver监听某个目录下文件是否发生变化,这里不言而喻了,就是/data/anr/xxx,如果当前文件夹中的文件发生变化,那么意味着ANR发生了,首先我们先了解一个这个类。

@Deprecated
public FileObserver(String path) {
    this(new File(path));
}

/**
 * Equivalent to FileObserver(file, FileObserver.ALL_EVENTS).
 */
public FileObserver(@NonNull File file) {
    this(Arrays.asList(file));
}

/**
 * Equivalent to FileObserver(paths, FileObserver.ALL_EVENTS).
 *
 * @param files The files or directories to monitor
 */
public FileObserver(@NonNull List<File> files) {
    this(files, ALL_EVENTS);
}

我们先看一下其中比较核心的构造方法,FileObserver能够监听某个路径下的文件、某个文件或者文件集合的变化,FileObserver是一个抽象类,那么我们可以实现它来监听anr目录文件的变化

@IntDef(flag = true, value = {
        ACCESS,
        MODIFY,
        ATTRIB,
        CLOSE_WRITE,
        CLOSE_NOWRITE,
        OPEN,
        MOVED_FROM,
        MOVED_TO,
        CREATE,
        DELETE,
        DELETE_SELF,
        MOVE_SELF
})

具体的文件状态有以上这些,包括ACCESS(当前文件被访问了)、MODIFY(当前文件被修改了)、CREATE(当前文件被创建了)、DELETE(当前文件被删除了)等等

class ANRFileObserver(
    val anrPath: String
) : FileObserver(anrPath) {


    override fun onEvent(event: Int, path: String?) {
        when(event){
            ACCESS->{

            }
            MODIFY->{
            }
            DELETE->{

            }
            CREATE->{
            }
        }
    }
}

这里主要是检测这4种状态,当前文件夹下内容有修改时,就将全部的trace文件上传到服务端进行日志查看。

val observer = ANRFileObserver("/data/anr/")
observer.startWatching()

但是这里需要注意的就是,很多高版本的ROM已经不支持当前文件夹的查看,甚至需要Root,因此此策略暂时不能应用,那么除此之外,还可以通过WatchDog来监控线程状态,从而判断是否发生ANR。

2.2.3 WatchDog

从字面意思上看,就是看门狗,其实这个是Android系统中的一种监控机制,当SystemServer进程启动,调用start方法之后,WatchDog也就启动了run方法

从上面这张图可以理解WatchDog的原理:首先WatchDog是一个线程,每隔5s发送一个Message消息到主线程的MessageQueue中,主线程Looper从消息队列中取出Message,如果没有阻塞,那么在5s内会执行这个Message任务,就没有ANR;如果超过5s没有执行,那么就有可能出现ANR。

到此这篇关于Android性能优化之ANR问题定位分析的文章就介绍到这了,更多相关Android ANR 内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Android性能优化之ANR问题定位分析的更多相关文章

  1. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  3. HTML5在微信内置浏览器下右上角菜单的调整字体导致页面显示错乱的问题

    HTML5在微信内置浏览器下,在右上角菜单的调整字体导致页面显示错乱的问题,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  4. ios – containerURLForSecurityApplicationGroupIdentifier:在iPhone和Watch模拟器上给出不同的结果

    我使用默认的XCode模板创建了一个WatchKit应用程序.我向iOSTarget,WatchkitAppTarget和WatchkitAppExtensionTarget添加了应用程序组权利.(这是应用程序组名称:group.com.lombax.fiveminutes)然后,我尝试使用iOSApp和WatchKitExtension访问共享文件夹URL:延期:iOS应用:但是,测试NSURL

  5. Ionic – Splash Screen适用于iOS,但不适用于Android

    我有一个离子应用程序,其中使用CLI命令离子资源生成的启动画面和图标iOS版本与正在渲染的启动画面完美配合,但在Android版本中,只有在加载应用程序时才会显示白屏.我检查了config.xml文件,所有路径看起来都是正确的,生成的图像出现在相应的文件夹中.(我使用了splash.psd模板来生成它们.我错过了什么?这是config.xml文件供参考,我觉得我在这里做错了–解决方法在config.xml中添加以下键:它对我有用!

  6. ios – 无法启动iPhone模拟器

    /Library/Developer/CoreSimulator/Devices/530A44CB-5978-4926-9E91-E9DBD5BFB105/data/Containers/Bundle/Application/07612A5C-659D-4C04-ACD3-D211D2830E17/ProductName.app/ProductName然后,如果您在Xcode构建设置中选择标准体系结构并再次构建和运行,则会产生以下结果:dyld:lazysymbolbindingFailed:Symbol

  7. Xamarin iOS图像在Grid内部重叠

    heyo,所以在Xamarin我有一个使用并在其中包含一对,所有这些都包含在内.这在Xamarin.Android中看起来完全没问题,但是在Xamarin.iOS中,图像与标签重叠.我不确定它的区别是什么–为什么它在Xamarin.Android中看起来不错但在iOS中它的全部都不稳定?

  8. 在iOS上向后播放HTML5视频

    我试图在iPad上反向播放HTML5视频.HTML5元素包括一个名为playbackRate的属性,它允许以更快或更慢的速率或相反的方式播放视频.根据Apple’sdocumentation,iOS不支持此属性.通过每秒多次设置currentTime属性,可以反复播放,而无需使用playbackRate.这种方法适用于桌面Safari,但似乎在iOS设备上的搜索限制为每秒1次更新–在我的情况下太慢了.有没有办法在iOS设备上向后播放HTML5视频?解决方法iOS6Safari现在支持playbackRat

  9. Swift思量与初探:我需要学习Swift吗?

    最近,除了N多的基于Swift的服务端开发框架,笔者不由深思,到底该这么评价Swift呢?前两点在Swift的语法和语言特性中已经表现得淋漓尽致:像是尾随闭包,枚举关联值,可选值和强制的类型安全等都是Swift显而易见的优点。综上所述,Swift拥有着被广泛使用以及当做第一学习语言的潜质。Swift在语法层次上会更加高级,并且Swift并没有使用GC机制,因此可以与C更好地相兼容。Swift中的注释与C语言的注释非常相似。

  10. 使用 Swift 语言编写 Android 应用入门

    Swift标准库可以编译安卓armv7的内核,这使得可以在安卓移动设备上执行Swift语句代码。做梦,虽然Swift编译器可以胜任在安卓设备上编译Swift代码并运行。这需要的不仅仅是用Swift标准库编写一个APP,更多的是你需要一些框架来搭建你的应用用户界面,以上这些Swift标准库不能提供。简单来说,构建在安卓设备上使用的Swiftstdlib需要libiconv和libicu。通过命令行执行以下命令:gitclonegit@github.com:SwiftAndroid/libiconv-libi

随机推荐

  1. Flutter 网络请求框架封装详解

    这篇文章主要介绍了Flutter 网络请求框架封装详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  2. Android单选按钮RadioButton的使用详解

    今天小编就为大家分享一篇关于Android单选按钮RadioButton的使用详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

  3. 解决android studio 打包发现generate signed apk 消失不见问题

    这篇文章主要介绍了解决android studio 打包发现generate signed apk 消失不见问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

  4. Android 实现自定义圆形listview功能的实例代码

    这篇文章主要介绍了Android 实现自定义圆形listview功能的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  5. 详解Android studio 动态fragment的用法

    这篇文章主要介绍了Android studio 动态fragment的用法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. Android用RecyclerView实现图标拖拽排序以及增删管理

    这篇文章主要介绍了Android用RecyclerView实现图标拖拽排序以及增删管理的方法,帮助大家更好的理解和学习使用Android,感兴趣的朋友可以了解下

  7. Android notifyDataSetChanged() 动态更新ListView案例详解

    这篇文章主要介绍了Android notifyDataSetChanged() 动态更新ListView案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

  8. Android自定义View实现弹幕效果

    这篇文章主要为大家详细介绍了Android自定义View实现弹幕效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  9. Android自定义View实现跟随手指移动

    这篇文章主要为大家详细介绍了Android自定义View实现跟随手指移动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  10. Android实现多点触摸操作

    这篇文章主要介绍了Android实现多点触摸操作,实现图片的放大、缩小和旋转等处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部