android anr定位分析_一个ANR的log带来的优化

ANR(Application Not Responding)在Android中比Crash更难以定位,尤其是涉及系统日志的情况。本文通过一个具体的ANR案例,分析了一个主线程响应超时但相关日志为系统代码的场景。通过观察其他线程,发现名为MTPrimaryEglEngine的线程在执行文件检查操作时引发问题。进一步测试发现`isExternalStorageReadable`方法的平均耗时高达3ms,而`File.exists()`则仅为0.1ms。意识到`isExternalStorageReadable`仅检查SD卡存在,不涉及权限后,进行了优化,改为静态变量一次性判断,显著减少了性能开销,提升了应用性能。
摘要由CSDN通过智能技术生成

相对于crash,anr是更难定位的,特别是一些都是系统log的anr,比如下面这种

#1 main
Input dispatching timed out
1 android.os.BinderProxy.transactNative(Native Method)
2 android.os.BinderProxy.transact(BinderProxy.java:510)
3 android.app.IActivityManager$Stub$Proxy.getCastPid(IActivityManager.java:9586)
4 android.hardware.SystemSensorManager$SensorEventQueue.dispatchSensorEvent(SystemSensorManager.java:831)
5 android.os.MessageQueue.nativePollOnce(Native Method)
6 android.os.MessageQueue.next(MessageQueue.java:336)
7 android.os.Looper.loop(Looper.java:181)
8 android.app.ActivityThread.main(ActivityThread.java:7520)
9 java.lang.reflect.Method.invoke(Native Method)
10 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
11 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)

主线程发了响应超时,但是相关的log都是系统代码,无法定位到问题,这个时候,可以看下其他线程的信息,常常会有意外的收获,比如下面这种

0c21909094b60e7033afb1f70ed4592e.png

可以发现,出现anr的时候,这里有名称叫MTPrimaryEglEngine的线程正在执行判断文件是否可用的方法,连续看了多个anr的log,都是上报同样的情况,相关代码如下

public static boolean isFileExist(String filePath) {
    if (TextUtils.isEmpty(filePath)) {
       return false;
    } else {
       return StorageUtils.isExternalStorageReadable() ? (new File(filePath)).exists() : false;
     }
    }
    
public static boolean isExternalStorageReadable() {
        String state = Environment.getExternalStorageState();
        return state.equals("mounted") || state.equals("mounted_ro");
    }

初步看代码,没发现什么问题,不过抱着怀疑的心态,新建一个demo跑下相关方法的耗时

330fe4788a1cc66be0e4ff15a8ee783a.png

time的后面是耗时,单位是纳秒,大吃一惊, isExternalStorageReadable方法平均耗时要3ms左右,而且isFileExist()方法一个高频调用的方法,每次调用判断,都会导致3ms左右的耗时,实在太严重了,同时,另外一个,判断文件存在的方法File.exist()只有0.1ms左右,就合理多了

接下来尝试手动关闭App的读取sd卡的权限,发现isExternalStorageReadable也是返回true,这个时候才突然醒悟

isExternalStorageReadable只判断当前手机是否有SD卡,跟是否拥有读SD卡的权限是两回事

这个代码也是很多年前的线上代码了,这么多年,一直在这里卡住了耗时,唏嘘不已,接下来就是立马做优化,基于最小改动原则,改成了一次性判断

    private static final boolean isStorageReadable;

    static {
        /**
         * 这个方法在麒麟890手机上验证平均耗时3ms
         * SD卡是否可用,只需要判断一次就可以,优化性能
         */
        final String state = Environment.getExternalStorageState();
        isStorageReadable = state.equals(Environment.MEDIA_MOUNTED) || state.equals(Environment.MEDIA_MOUNTED_READ_ONLY);
    }
    
public static boolean isExternalStorageReadable() {
        return isStorageReadable;
    }

用static修饰,保证只执行一次,把结果存在一个final值里面(本来想用kotlin的by lazy的,无奈这个库是多项目公用,还是用java)

这样的话,下次每次判断,只需要获取当前的boolean值,非常的轻量,问题修复,由于这个方法很多地方都会频繁调用,上线后,应该会对App整体的性能有一些优化

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值