广告位联系
返回顶部
分享到

Android性能优化之ANR问题定位分析

Android 来源:互联网 作者:佚名 发布时间:2022-08-28 21:02:14 人浏览
摘要

前言 ANR(Application Not Response)应用程序未响应,当主线程被阻塞时,就会弹出如下弹窗 要么关闭当前app,要么就等待,其实这个时候没有挽救的措施,选择等待最终的结果也是ANR,最

前言

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

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

1

2

3

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吗?

1

2

3

4

btnSend.setOnClickListener {

    Thread.sleep(5 * 1000)

    ToastUtils.setText(this)

}

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

1.2 BroadCastTimeout

1

2

3

4

5

class MyBroadCast : BroadcastReceiver(){

    override fun onReceive(context: Context?, intent: Intent?) {

        //TODO 接收广播

    }

}

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

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

1.3 ServiceTimeout

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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发生了,首先我们先了解一个这个类。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

@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目录文件的变化

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@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(当前文件被删除了)等等

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

class ANRFileObserver(

    val anrPath: String

) : FileObserver(anrPath) {

 

 

    override fun onEvent(event: Int, path: String?) {

        when(event){

            ACCESS->{

 

            }

            MODIFY->{

            }

            DELETE->{

 

            }

            CREATE->{

            }

        }

    }

}

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

1

2

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。


版权声明 : 本文内容来源于互联网或用户自行发布贡献,该文观点仅代表原作者本人。本站仅提供信息存储空间服务和不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权, 违法违规的内容, 请发送邮件至2530232025#qq.cn(#换@)举报,一经查实,本站将立刻删除。
原文链接 : https://juejin.cn/post/7136534172096528398
相关文章
  • Kotlin的Collection与Sequence操作异同点介绍

    Kotlin的Collection与Sequence操作异同点介绍
    在Android开发中,集合是我们必备的容器,Kotlin的标准库中提供了很多处理集合的方法,而且还提供了两种基于容器的工作方式:Collection 和
  • 实现一个Kotlin函数类型方法

    实现一个Kotlin函数类型方法
    接口与函数类型 业务开发中,经常会有实现一个函数式接口(即接口只有一个方法需要实现)的场景,大家应该都会不假思索的写出如下代
  • Android10 App启动Activity源码分析
    ActivityThread的main方法 让我们把目光聚焦到ActivityThread的main方法上。 ActivityThread的源码路径为/frameworks/base/core/java/android/app/ActivityThread。 1 2
  • Android10客户端事务管理ClientLifecycleManager源码解析

    Android10客户端事务管理ClientLifecycleManager源码解析
    在Android 10 App启动分析之Activity启动篇(二)一文中,简单地介绍了Activity的生命周期管理器是如何调度Activity进入onCreate生命周期的流程。这
  • Kotlin对象的懒加载方式by lazy与lateinit异同介绍

    Kotlin对象的懒加载方式by lazy与lateinit异同介绍
    属性或对象的延时加载是我们相当常用的,一般我们都是使用 lateinit 和 by lazy 来实现。 他们两者都是延时初始化,那么在使用时那么他们两
  • Android类加载流程分析

    Android类加载流程分析
    本文分析的代码基于Android8.1.0源码。 流程分析 从loadClass开始,我们来看下Android中类加载的流程 /libcore/ojluni/src/main/java/java/lang/ClassLoader.ja
  • Android实现读写USB串口数据的代码

    Android实现读写USB串口数据的代码
    最近在研究USB方面的内容;先后做了关于Android读写HID、串口设备的DEMO。本文比较简单,主要介绍的是Android实现读取串口数据的功能 废话不
  • Epoxy - 在RecyclerView中构建复杂界面
    Diffing 对于复杂数据结构支持的多个视图类型展示在屏幕上, Epoxy此时是尤其有用的. 在这些场景中, 数据可能会被网络请求, 异步 Observable, 用
  • Android性能优化的详细介绍

    Android性能优化的详细介绍
    性能优化是一个app很重要的一部分,一个性能优良的app从被下载到启动到使用都能给用户到来很好的体验。自然我们做性能优化也是从被下
  • Android进阶宝典-插件化2(Hook启动插件中四大组件

    Android进阶宝典-插件化2(Hook启动插件中四大组件
    在上一节,我们主要介绍了如果通过反射来加载插件中的类,调用类中的方法;既然插件是一个apk,其实最重要的是启动插件中的Activity、
  • 本站所有内容来源于互联网或用户自行发布,本站仅提供信息存储空间服务,不拥有版权,不承担法律责任。如有侵犯您的权益,请您联系站长处理!
  • Copyright © 2017-2022 F11.CN All Rights Reserved. F11站长开发者网 版权所有 | 苏ICP备2022031554号-1 | 51LA统计