一篇文章搞定《Android中的ANR》

news2024/11/27 9:45:20

------《ANR》

  • 什么是ANR
  • 举个例子帮你认识ANR
  • ANR的产生原因
  • ANR的监控手段
    • 方法一: 监控trace文件夹
    • 方法二:利用我们主线程的Looper
    • 方法三:监控SIGQUIT信号
  • ANR日志Traces.txt
    • Traces文件分析
    • 几个分析案例:
      • 一、好定位的问题(简单案例)
      • 二、不好定位的
        • 主线程被锁阻塞
        • CPU被抢占
        • 内存紧张导致ANR
        • 系统服务超时导致ANR
  • 总结

什么是ANR

是系统通过与之交互的组件以及用户交互进行超时监控,用来判断应用进程是否存在卡死或响应过慢的问题,通俗来说就是很多系统中看门狗(watchdog)的设计思想。

举个例子帮你认识ANR

问题:在Activity的onCreate方法里调用sleep方法会发生ANR吗?
大多人可能会认为只要在主线程做了耗时操作就会发生ANR,那么真的是这样吗?
在Activity的onCreate方法里调用Thread.sleep(20 * 1000)让主线程sleep20秒,会导致应用程序ANR吗?写个Demo测试一下。

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        try {
            Log.d("ANR","开始sleep");
            Thread.sleep(20*1000);
            Log.d("ANR","结束sleep");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

如上代码,运行程序,结果应用没有发生ANR,在sleep了60秒后正常打印日志。
在这里插入图片描述
看到了吗!看到了吗! 躺平了20秒后又继续了。
让我们再运行一遍,与上次吧不同的是。我们在sleep的20种,触发一个返回事件。
在这里插入图片描述
看到了吗!看到了吗! ANR崩溃了!
说明了什么兄弟们:从上面的结果,我们知道了其实在主线程sleep方法或者说做耗时操作,不一定会产生ANR
导致ANR的根本还是应用程序无法在一定时间内响应用户的操作。因为主线程被占用了。聪明的兄弟们应该已经知道为什么了,没错就是我们主线程的Handler And Looper。没办法去处理下一个Message。

ANR的产生原因

  • Service Timeout:比如前台服务在20s内未执行完成,后台服务Timeout时间是前台服务的10倍,200s;
  • BroadcastQueue Timeout:比如前台广播在10s内未执行完成,后台60s
  • ContentProvider Timeout:内容提供者,在publish过超时10s;
  • InputDispatching Timeout: 输入事件分发超时5s,包括按键和触摸事件。
//ActiveServices.java
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
// How long the startForegroundService() grace period is to get around to
// calling startForeground() before we ANR + stop it.
static final int SERVICE_START_FOREGROUND_TIMEOUT = 10*1000;

//ActivityManagerService.java
// How long we allow a receiver to run before giving up on it.
static final int BROADCAST_FG_TIMEOUT = 10*1000;
static final int BROADCAST_BG_TIMEOUT = 60*1000;
// How long we wait until we timeout on key dispatching.
static final int KEY_DISPATCHING_TIMEOUT = 5*1000;

ANR的监控手段

方法一: 监控trace文件夹

权限问题只适用于Android6之前
Android M(6.0) 版本之后,应用侧无法直接通过监听 data/anr/trace 文件,监控是否发生 ANR。

方法二:利用我们主线程的Looper

先看一下我们的Looper流程

public static void loop() {
    for (;;) {
        //1、取消息
        Message msg = queue.next(); // might block
        ...
        //2、消息处理前回调
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        ...

        //3、消息开始处理
        msg.target.dispatchMessage(msg);// 分发处理消息
        ...

        //4、消息处理完回调
        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }
    }
    ...
}

聪明的兄弟们已经想到了:可以利用注释2、4去获取我们处理消息的时间。从而判断我们的ANR情况。

我们只需要调用Looper.getMainLooper().setMessageLogging(printer),即可从回调中拿到Handler处理一个消息的前后时间。

需要注意的是,监听到发生卡顿之后,dispatchMessage 早已调用结束,已经出栈,此时再去获取主线程堆栈,堆栈中是不包含卡顿的代码的。

所以需要在后台开一个线程,定时获取主线程堆栈,将时间点作为key,堆栈信息作为value,保存到Map中,在发生卡顿的时候,取出卡顿时间段内的堆栈信息即可。

不过这种方案只适合线下使用,原因如下:

  1. logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);存在字符串拼接,频繁调用,会创建大量对象,造成内存抖动。
  2. 后台线程频繁获取主线程堆栈,对性能有一定影响,获取主线程堆栈,会暂停主线程的运行。

方法三:监控SIGQUIT信号

这种方案才是真正的监控ANR,matrix、xCrash都在使用这种方案。已经在国民应用微信等app上检验过,稳定性和可靠性都能得到保证。

ANR流程基本是在system_server系统进程完成的,系统进程的行为我们很难监控到,想要监控这个事情就得从系统进程与应用进程沟通的边界着手,看边界上有没有可以操作的地方。
现在,我们整理一下整个ANR的流程:

  1. 系统监控到app发生ANR后,收集了一些相关进程pid(包括发生ANR的进程),准备让这些进程dump堆栈,从而生成ANR Trace文件
  2. 系统开始向这些进程发送SIGQUIT信号,进程收到SIGQUIT信号之后开始dump堆栈
    微信团队解析ANR的流程示意图:
    在这里插入图片描述
    可以看到,一个进程发生ANR之后的整个流程,只有dump堆栈的行为会发生在发生ANR的进程中,其他过程全在系统进程进行处理的,我们无法感知。这个过程从收到SIGQUIT信号开始到使用socket写Trace结束。然后继续回到系统进程完成剩余的ANR流程,这2个边界上我们可以做做文章。

ANR日志Traces.txt

首先traces.txt文件位置在/data/anr/目录下,可以通过以下adb命令将其拷贝到sd卡目录下获取查看。

分析traces文件来定位ANR大多数适用于线下,当然我们可以在线上将我们的traces.txt。但是我们需要这么做:
1、当监控线程发现主线程卡死时,主动向系统发送SIGNAL_QUIT信号。
2、等待/data/anr/traces.txt文件生成。
3、文件生成以后进行上报。

看起来好像可行,不过有以下两个问题:
1、traces.txt 里面包含所有线程的信息,上传之后需要人工过滤分析
2、很多高版本系统需要root权限才能读取 /data/anr这个目录
在这里插入图片描述

Traces文件分析

拿到trace文件,详细分析下:

----- pid 7761 at 2022-11-02 07:02:26 -----
Cmd line: com.xfhy.watchsignaldemo
Build fingerprint: 'HUAWEI/LYA-AL00/HWLYA:10/HUAWEILYA-AL00/10.1.0.163C00:user/release-keys'
ABI: 'arm64'
Build type: optimized
Zygote loaded classes=11918 post zygote classes=729
Dumping registered class loaders
#0 dalvik.system.PathClassLoader: [], parent #1
#1 java.lang.BootClassLoader: [], no parent
#2 dalvik.system.PathClassLoader: [/system/app/FeatureFramework/FeatureFramework.apk], no parent
#3 dalvik.system.PathClassLoader: [/data/app/com.xfhy.watchsignaldemo-4tkKMWojrpHAf-Q3iecaHQ==/base.apk:/data/app/com.xfhy.watchsignaldemo-4tkKMWojrpHAf-Q3iecaHQ==/base.apk!classes2.dex:/data/app/com.xfhy.watchsignaldemo-4tkKMWojrpHAf-Q3iecaHQ==/base.apk!classes4.dex:/data/app/com.xfhy.watchsignaldemo-4tkKMWojrpHAf-Q3iecaHQ==/base.apk!classes3.dex], parent #1
Done dumping class loaders
Intern table: 44132 strong; 436 weak
JNI: CheckJNI is off; globals=681 (plus 67 weak)
Libraries: /data/app/com.xfhy.watchsignaldemo-4tkKMWojrpHAf-Q3iecaHQ==/lib/arm64/libwatchsignaldemo.so libandroid.so libcompiler_rt.so libhitrace_jni.so libhiview_jni.so libhwapsimpl_jni.so libiAwareSdk_jni.so libimonitor_jni.so libjavacore.so libjavacrypto.so libjnigraphics.so libmedia_jni.so libopenjdk.so libsoundpool.so libwebviewchromium_loader.so (15)
//已分配堆内存大小26M,其中2442kb医用,总分配74512个对象
Heap: 90% free, 2442KB/26MB; 74512 objects

Total number of allocations 120222 //进程创建到现在一共创建了多少对象
Total bytes allocated 10MB         //进程创建到现在一共申请了多少内存
Total bytes freed 8173KB           //进程创建到现在一共释放了多少内存
Free memory 23MB                   //不扩展堆的情况下可用的内存
Free memory until GC 23MB          //GC前的可用内存
Free memory until OOME 381MB       //OOM之前的可用内存,这个值很小的话,说明已经处于内存紧张状态,app可能是占用了过多的内存
Total memory 26MB                  //当前总内存(已用+可用)
Max memory 384MB                   //进程最多能申请的内存

.....//省略GC相关信息


//当前进程共17个线程
DALVIK THREADS (17):

//Signal Catcher线程调用栈
"Signal Catcher" daemon prio=5 tid=4 Runnable
  | group="system" sCount=0 dsCount=0 flags=0 obj=0x18c84570 self=0x7252417800
  | sysTid=7772 nice=0 cgrp=default sched=0/0 handle=0x725354ad50
  | state=R schedstat=( 16273959 1085938 5 ) utm=0 stm=1 core=4 HZ=100
  | stack=0x7253454000-0x7253456000 stackSize=991KB
  | held mutexes= "mutator lock"(shared held)
  native: #00 pc 000000000042f8e8  /apex/com.android.runtime/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, int, BacktraceMap*, char const*, art::ArtMethod*, void*, bool)+140)
  native: #01 pc 0000000000523590  /apex/com.android.runtime/lib64/libart.so (art::Thread::DumpStack(std::__1::basic_ostream<char, std::__1::char_traits<char>>&, bool, BacktraceMap*, bool) const+508)
  native: #02 pc 000000000053e75c  /apex/com.android.runtime/lib64/libart.so (art::DumpCheckpoint::Run(art::Thread*)+844)
  native: #03 pc 000000000053735c  /apex/com.android.runtime/lib64/libart.so (art::ThreadList::RunCheckpoint(art::Closure*, art::Closure*)+504)
  .....
  (no managed stack frames)

"main" prio=5 tid=1 Sleeping
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x73907540 self=0x725f010800
  | sysTid=7761 nice=-10 cgrp=default sched=1073741825/2 handle=0x72e60080d0
  | state=S schedstat=( 281909898 5919799 311 ) utm=20 stm=7 core=4 HZ=100
  | stack=0x7fca180000-0x7fca182000 stackSize=8192KB
  | held mutexes=
  at java.lang.Thread.sleep(Native method)
  - sleeping on <0x00f895d9> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:443)
  - locked <0x00f895d9> (a java.lang.Object)
  at java.lang.Thread.sleep(Thread.java:359)
  at android.os.SystemClock.sleep(SystemClock.java:131)
  at com.xfhy.watchsignaldemo.MainActivity.makeAnr(MainActivity.kt:35)
  ... //此处省略剩余的N个线程

trace参数详细解读:

"Signal Catcher" daemon prio=5 tid=4 Runnable
  | group="system" sCount=0 dsCount=0 flags=0 obj=0x18c84570 self=0x7252417800
  | sysTid=7772 nice=0 cgrp=default sched=0/0 handle=0x725354ad50
  | state=R schedstat=( 16273959 1085938 5 ) utm=0 stm=1 core=4 HZ=100
  | stack=0x7253454000-0x7253456000 stackSize=991KB
  | held mutexes= "mutator lock"(shared held)

Signal Catcher" daemon prio=5 tid=4 Runnable

  • “Signal Catcher” daemon : 线程名,有daemon表示守护线程
  • prio:线程优先级
  • tid:线程内部id
  • 线程状态:Runnable
    在这里插入图片描述

一般来说:main线程处于BLOCK、WAITING、TIMEWAITING状态,基本上是函数阻塞导致的ANR,如果main线程无异常,则应该排查CPU负载和内存环境。

几个分析案例:

一、好定位的问题(简单案例)

首先看到Loacat日志:

07-22 21:39:17.019 819-851/? E/ActivityManager: ANR in com.xxxx.performance (com.xxxx.performance/.view.home.activity.MainActivity)
PID: 7398
Reason: Input dispatching timed out (com.xxxx.performance/com.xxxx.performance.view.home.activity.MainActivity, 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: 29.  Wait queue head age: 8579.3ms.)
Load: 18.22 / 18.1 / 18.18
CPU usage from 0ms to 8653ms later:
124% 7398/com.xxxx.performance: 118% user + 6.5% kernel / faults: 4962 minor 7 major
82% 819/system_server: 28% user + 53% kernel / faults: 10555 minor 11 major
23% 4402/adbd: 1% user + 22% kernel
10% 996/com.android.systemui: 4.6% user + 6.2% kernel / faults: 4677 minor 1 major
4.6% 2215/com.android.phone: 1.5% user + 3.1% kernel / faults: 5411 minor
6.3% 6268/perfd: 3.4% user + 2.8% kernel / faults: 134 minor
0.5% 1149/com.miui.whetstone: 0.1% user + 0.3% kernel / faults: 3016 minor 1 major
0.2% 2097/com.xiaomi.finddevice: 0.1% user + 0.1% kernel / faults: 2256 minor
0.6% 2143/com.miui.daemon: 0.2% user + 0.3% kernel / faults: 2798 minor
1.2% 1076/com.xiaomi.xmsf: 0.6% user + 0.6% kernel / faults: 2802 minor
......

从日志第一行开始看,可以看到发生错误的应用包名和类名,这里是

ANR in com.xxxx.performance (com.xxxx.performance/.view.home.activity.MainActivity)

接着看到进程号PID为7398。发生ANR的Reason是Input dispatching timed out就是上面提到的第一种。再往下就是活跃进程的CPU占用率日志。

光看Logcat中的日志只能看到这些信息,大概知道是在MainActivity出现了问题,但还是不能清楚的定位到发生ANR的代码行,想要获得进一步的错误信息只能通过查看ANR过程中生成的堆栈信息文件traces.txt了。
traces.txt里的信息:

DALVIK THREADS (42):
"main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 obj=0x75ceafb8 self=0x55933ae7e0
| sysTid=7398 nice=0 cgrp=default sched=0/0 handle=0x7f7ddae0f0
| state=S schedstat=( 101485399944 3411372871 31344 ) utm=9936 stm=212 core=1 HZ=100
| stack=0x7fc8d40000-0x7fc8d42000 stackSize=8MB
| held mutexes=
kernel: __switch_to+0x74/0x8c
kernel: futex_wait_queue_me+0xcc/0x158
kernel: futex_wait+0x120/0x20c
kernel: do_futex+0x184/0xa48
kernel: SyS_futex+0x88/0x19c
kernel: cpu_switch_to+0x48/0x4c
native: #00 pc 00017750  /system/lib64/libc.so (syscall+28)
native: #01 pc 000d1584  /system/lib64/libart.so (_ZN3art17ConditionVariable4WaitEPNS_6ThreadE+140)
native: #02 pc 00388098  /system/lib64/libart.so (_ZN3artL12GoToRunnableEPNS_6ThreadE+1068)
native: #03 pc 000a5db8  /system/lib64/libart.so (_ZN3art12JniMethodEndEjPNS_6ThreadE+24)
native: #04 pc 000280e4  /data/dalvik-cache/arm64/system@framework@boot.oat (Java_android_graphics_Paint_native_1init__+156)
at android.graphics.Paint.native_init(Native method)
at android.graphics.Paint.<init>(Paint.java:435)
at android.graphics.Paint.<init>(Paint.java:425)
at android.text.TextPaint.<init>(TextPaint.java:49)
at android.text.Layout.<init>(Layout.java:160)
at android.text.StaticLayout.<init>(StaticLayout.java:111)
at android.text.StaticLayout.<init>(StaticLayout.java:87)
at android.text.StaticLayout.<init>(StaticLayout.java:66)
at android.widget.TextView.makeSingleLayout(TextView.java:6543)
at android.widget.TextView.makeNewLayout(TextView.java:6383)
at android.widget.TextView.checkForRelayout(TextView.java:7096)
at android.widget.TextView.setText(TextView.java:4082)
at android.widget.TextView.setText(TextView.java:3940)
at android.widget.TextView.setText(TextView.java:3915)
at com.xxxx.performance.view.home.fragment.AttendanceCheckInFragment.onNowTimeSuccess(AttendanceCheckInFragment.java:887)
at com.xxxx.performance.presenter.attendance.AttendanceFragmentPresenter$6.onNext(AttendanceFragmentPresenter.java:214)
at com.xxxx.performance.presenter.attendance.AttendanceFragmentPresenter$6.onNext(AttendanceFragmentPresenter.java:205)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.drainNormal(ObservableObserveOn.java:198)
at io.reactivex.internal.operators.observable.ObservableObserveOn$ObserveOnObserver.run(ObservableObserveOn.java:250)
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
......
......

还是从头开始看,来看每个字段对应的含义:

线程名:main
线程优先级:prio=5
线程锁ID: tid=1
线程状态:Native
线程组名称:group=“main”
线程被挂起的次数:sCount=1
线程被调试器挂起的次数:dsCount=0
线程的java的对象地址:obj=0x75ceafb8
线程本身的Native对象地址:self=0x55933ae7e0
###################################
线程调度信息:
Linux系统中内核线程ID:sysTid=7398与主线程的进程号相同
线程调度优先级:nice=0
线程调度组:cgrp=default
线程调度策略和优先级:sched=0/0
线程处理函数地址:handle=0x7f7ddae0f0
######################################
线程的上下文信息:
线程调度状态:state=S
线程在CPU中的执行时间、线程等待时间、线程执行的时间片长度:schedstat=(1014853999443411372871 31344 )
线程在用户态中的调度时间值:utm=9936
线程在内核态中的调度时间值:stm=212
最后执行这个线程的CPU核序号:core=1
#######################################
线程的堆栈信息:
堆栈地址和大小:stack=0x7fc8d40000-0x7fc8d42000 stackSize=8MB

最后看到堆栈信息里的这一行:

at com.xxxx.performance.view.home.fragment.AttendanceCheckInFragment.onNowTimeSuccess(AttendanceCheckInFragment.java:887)

这里就看清楚了是在AttendanceCheckInFragment中的887行出现的问题,再到对应代码行中就很容易发现ANR的原因了。

二、不好定位的

主线程被锁阻塞

fun makeAnr(view: View) {

    val obj1 = Any()
    val obj2 = Any()

    //搞个死锁,相互等待

    thread(name = "卧槽") {
        synchronized(obj1) {
            SystemClock.sleep(100)
            synchronized(obj2) {
            }
        }
    }

    synchronized(obj2) {
        SystemClock.sleep(100)
        synchronized(obj1) {
        }
    }
}
"main" prio=5 tid=1 Blocked
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x73907540 self=0x725f010800
  | sysTid=19900 nice=-10 cgrp=default sched=0/0 handle=0x72e60080d0
  | state=S schedstat=( 542745832 9516666 182 ) utm=48 stm=5 core=4 HZ=100
  | stack=0x7fca180000-0x7fca182000 stackSize=8192KB
  | held mutexes=
  at com.xfhy.watchsignaldemo.MainActivity.makeAnr(MainActivity.kt:59)
  - waiting to lock <0x0c6f8c52> (a java.lang.Object) held by thread 22   //注释1
  - locked <0x01abeb23> (a java.lang.Object)
  at java.lang.reflect.Method.invoke(Native method)
  at androidx.appcompat.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:441)
  at android.view.View.performClick(View.java:7317)
  at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1219)
  at android.view.View.performClickInternal(View.java:7291)
  at android.view.View.access$3600(View.java:838)
  at android.view.View$PerformClick.run(View.java:28247)
  at android.os.Handler.handleCallback(Handler.java:900)
  at android.os.Handler.dispatchMessage(Handler.java:103)
  at android.os.Looper.loop(Looper.java:219)
  at android.app.ActivityThread.main(ActivityThread.java:8668)
  at java.lang.reflect.Method.invoke(Native method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1109)

"卧槽" prio=5 tid=22 Blocked  //注释2
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x12c8a118 self=0x71d625f800
  | sysTid=20611 nice=0 cgrp=default sched=0/0 handle=0x71d4513d50
  | state=S schedstat=( 486459 0 3 ) utm=0 stm=0 core=4 HZ=100
  | stack=0x71d4411000-0x71d4413000 stackSize=1039KB
  | held mutexes=
  at com.xfhy.watchsignaldemo.MainActivity$makeAnr$1.invoke(MainActivity.kt:52)
  - waiting to lock <0x01abeb23> (a java.lang.Object) held by thread 1
  - locked <0x0c6f8c52> (a java.lang.Object)  
  at com.xfhy.watchsignaldemo.MainActivity$makeAnr$1.invoke(MainActivity.kt:49)
  at kotlin.concurrent.ThreadsKt$thread$thread$1.run(Thread.kt:30)

......

注意看,下面几行:

"main" prio=5 tid=1 Blocked
  - waiting to lock <0x0c6f8c52> (a java.lang.Object) held by thread 22
  - locked <0x01abeb23> (a java.lang.Object)

"卧槽" prio=5 tid=22 Blocked
  - waiting to lock <0x01abeb23> (a java.lang.Object) held by thread 1
  - locked <0x0c6f8c52> (a java.lang.Object)  

主线程的tid是1,线程状态是Blocked,正在等待0x0c6f8c52这个Object,而这个Object被thread 22这个线程所持有,主线程当前持有的是0x01abeb23的锁。而卧槽的tid是22,也是Blocked状态,它想请求的和已有的锁刚好与主线程相反。这样的话,ANR原因也就找到了:线程22持有了一把锁,并且一直不释放,主线程等待这把锁发生超时。在线上环境,常见因锁而ANR的场景是SharePreference写入。

CPU被抢占

CPU usage from 0ms to 10625ms later (2020-03-09 14:38:31.633 to 2020-03-09 14:38:42.257):
  543% 2045/com.test.demo: 54% user + 89% kernel / faults: 4608 minor 1 major //注意看这里
  99% 674/android.hardware.camera.provider@2.4-service: 81% user + 18% kernel / faults: 403 minor
  24% 32589/com.wang.test: 22% user + 1.4% kernel / faults: 7432 minor 1 major
  ......

可以看到,该进程占据CPU高达543%,抢占了大部分CPU资源,因为导致发生ANR,这种ANR与我们的app无关。

内存紧张导致ANR

如果一份ANR日志的CPU和堆栈都很正常,可以考虑是内存紧张。看一下ANR日志里面的内存相关部分。还可以去日志里面搜一下onTrimMemory,如果dump ANR日志的时间附近有相关日志,可能是内存比较紧张了。

10-31  E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0
10-31  E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0
10-31  E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0
10-31  E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0
10-31  E Runtime : onTrimMemory level:80,pid:com.xxx.xxx:Launcher0

系统服务超时导致ANR

"main" prio=5 tid=1 Native
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x727851e8 self=0x78d7060e00
  | sysTid=4894 nice=0 cgrp=default sched=0/0 handle=0x795cc1e9a8
  | state=S schedstat=( 8292806752 1621087524 7167 ) utm=707 stm=122 core=5 HZ=100
  | stack=0x7febb64000-0x7febb66000 stackSize=8MB
  | held mutexes=
  kernel: __switch_to+0x90/0xc4
  kernel: binder_thread_read+0xbd8/0x144c
  kernel: binder_ioctl_write_read.constprop.58+0x20c/0x348
  kernel: binder_ioctl+0x5d4/0x88c
  kernel: do_vfs_ioctl+0xb8/0xb1c
  kernel: SyS_ioctl+0x84/0x98
  kernel: cpu_switch_to+0x34c/0x22c0
  native: #00 pc 000000000007a2ac  /system/lib64/libc.so (__ioctl+4)
  native: #01 pc 00000000000276ec  /system/lib64/libc.so (ioctl+132)
  native: #02 pc 00000000000557d4  /system/lib64/libbinder.so (android::IPCThreadState::talkWithDriver(bool)+252)
  native: #03 pc 0000000000056494  /system/lib64/libbinder.so (android::IPCThreadState::waitForResponse(android::Parcel*, int*)+60)
  native: #04 pc 00000000000562d0  /system/lib64/libbinder.so (android::IPCThreadState::transact(int, unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+216)
  native: #05 pc 000000000004ce1c  /system/lib64/libbinder.so (android::BpBinder::transact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+72)
  native: #06 pc 00000000001281c8  /system/lib64/libandroid_runtime.so (???)
  native: #07 pc 0000000000947ed4  /system/framework/arm64/boot-framework.oat (Java_android_os_BinderProxy_transactNative__ILandroid_os_Parcel_2Landroid_os_Parcel_2I+196)
  at android.os.BinderProxy.transactNative(Native method) ————————————————关键行!!!
  at android.os.BinderProxy.transact(Binder.java:804)
  at android.net.IConnectivityManager$Stub$Proxy.getActiveNetworkInfo(IConnectivityManager.java:1204)—关键行!
  at android.net.ConnectivityManager.getActiveNetworkInfo(ConnectivityManager.java:800)
  at com.xiaomi.NetworkUtils.getNetworkInfo(NetworkUtils.java:2)
  at com.xiaomi.frameworkbase.utils.NetworkUtils.getNetWorkType(NetworkUtils.java:1)
  at com.xiaomi.frameworkbase.utils.NetworkUtils.isWifiConnected(NetworkUtils.java:1)

从日志堆栈中可以看到是获取网络信息发生了ANR:getActiveNetworkInfo。系统的服务都是Binder机制(16个线程),服务能力也是有限的,有可能系统服务长时间不响应导致ANR。如果其他应用占用了所有Binder线程,那么当前应用只能等待。可进一步搜索:blockUntilThreadAvailable关键字:

at android.os.Binder.blockUntilThreadAvailable(Native method)

如果有发现某个线程的堆栈,包含此字样,可进一步看其堆栈,确定是调用了什么系统服务。此类ANR也是属于系统环境的问题,如果某类型手机上频繁发生此问题,应用层可以考虑规避策略。

总结

大部分同学可能都是做业务需求为主,对于ANR问题,可能不太注重,或者直接依赖第三方,例如Bugly,但是呢,在面试中,面试官基本不太会问你这些工具的使用。所以还是学习一下吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/529092.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用FastGithub解决国内访问GitHub失败的问题

问题提出 在ArduinoIDE安装开发板开发包、库时经常由于这些包和库的索引指向的是github下面的开源项目&#xff0c;所以安装失败的可能性极高。开启了FastGithub后&#xff0c;更新成功率和速度都快了很多&#xff01; 问题解决 使用FastGithub开源工具&#xff08;支持Win,Ma…

在北京,36岁的软件测试人的心路历程及学习经验,太现实了

前言 涛哥&#xff08;我认识的一位朋友&#xff0c;也是我的前辈&#xff09;出身普通&#xff0c;仅仅是一个普通二本学校毕业&#xff0c;大学也是混日子混过去的&#xff0c;年轻时没有好好学习&#xff0c;被美女、游戏吸引到迷了自我&#xff0c;他对那年毕业的记忆清晰…

jpa使用

jpa&#xff1a; java persistence api jpa只要一对多 在一对多的关系中&#xff0c;一般将 JoinColumn 放在 多端&#xff0c;, 如果不需要两边都映射对应的实体&#xff0c;&#xff0c;可以在一端设置JoinColumn ,并在多端指定一个外键属性来映射这个关系

农业病虫虚拟仿真教学平台使实验资源共享

动物直肠检查是一项常见的内窥镜检查手术&#xff0c;往往会因为实验设备、实验动物、时间、经费等方面的因素影响&#xff0c;使一些应该开设的手术教学开展较少或者无法进行&#xff0c;造成学生或从业人士对专业知识掌握以及动手实践能力的不足 因此对于新手的培训必须经过大…

MISC:HTTP 流量分析技术.

MISC&#xff1a;HTTP 流量分析技术. Misc即杂项&#xff0c;是信息隐藏又称信息伪装&#xff0c;就是通过减少载体的某种冗余&#xff0c;如空间冗余、数据冗余等&#xff0c;来隐藏敏感信息&#xff0c;达到某种特殊的目的。 信息隐藏打破了传统密码学的思维范畴&#xff0c…

短期光伏发电量短期预测(Python代码,主要模型LSTM)

1.数据集&#xff08;68779条数据&#xff09; 开始时间 DATE_TIMEPLANT_IDSOURCE_KEYDC_POWERAC_POWERDAILY_YIELDTOTAL_YIELD15-05-2020 00:0041350011BY6WEcLGh8j5v7000625955915-05-2020 00:0041350011IF53ai7Xc0U56Y000618364515-05-2020 00:0041350013PZuoBAID5Wc2HD00…

你真的会写接口自动化测试脚本?0-1精通自动化测试实战,暴涨18K...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 开头&#xff0c;…

【Linux进阶命令 01】grep(文本的全局搜索与打印)

文章目录 一、grep命令&#xff08;全局搜索与打印&#xff09;1.1 语法1.2 主要参数1.3 测试准备1.4 grep命令使用示例1.5 应用示例 一、grep命令&#xff08;全局搜索与打印&#xff09; grep &#xff08;缩写来自Globally search a Regular Expression and Print&#xff0…

一文解决eBpf在Android上的集成和调试

eBPF&#xff08;Extended Berkeley Packet Filter &#xff09;是一种新兴的linux内核功能扩展技术&#xff0c;可以无需修改内核代码&#xff0c;在保证安全的前提下&#xff0c;灵活的动态加载程序&#xff0c;实现对内核功能的扩展。 Android平台上也引入了对eBpf技术的支…

NC 人力薪酬管理薪资发放流程粗略整理

NC 人力薪酬管理薪资发放流程: 1、【公共薪资项目-集团/组织】节点新增公共新增项目 2、【薪资期间-集团/组织】节点设置薪资期间 3、根据公司实际情况,如果需要,则在【薪资标准设置-集团/组织】、【薪资规则-集团/组织】两个节点设置薪资标准和薪资规则 4、在【税率表-集…

【类和对象(下)】

文章目录 &#x1f355;前言一、&#x1f355;再谈构造函数1.1构造函数体赋值1.2初始化列表1.3explicit关键字 二、&#x1f355;static成员三、&#x1f355;友元四、&#x1f355;内部类五、&#x1f355;匿名对象六、&#x1f355;拷贝对象时编译器的一些优化七、&#x1f3…

香港Web3,已走至黎明前夜 能否抓住机遇,是外界始终关注的话题?

香港对Web3的全面开放&#xff0c;既是挑战&#xff0c;又是一个不容错过的发展机遇。香港相关行业是否有足够的基础和能力抓住金融大变局的历史性契机&#xff0c;面向未来&#xff0c;完善金融生态&#xff0c;提升香港金融中心的国际竞争力&#xff0c;是外界始终关注的话题…

mongodb geohash

地理位置索引支持是MongoDB的一大亮点&#xff0c;这也是全球最流行的LBS服务foursquare 选择MongoDB的原因之一。我们知道&#xff0c;通常的数据库索引结构是B Tree&#xff0c;如何将地理位置转化为可建立BTree的形式&#xff0c;下文将为你描述。 首先假设我们将需要索引的…

Apache Doris简单易用、高性能和统一的分析数据库

Doris 介绍 https://github.com/apache/doris Apache Doris 是一个基于 MPP 架构的高性能、实时的分析型数据库&#xff0c;以极速易用的特点被人们所熟知&#xff0c;仅需亚秒级响应时间即可返回海量数据下的查询结果&#xff0c;不仅可以支持高并发的点查询场景&#xff0c;…

认识BACnet协议

一、什么是BACnet&#xff1f; BACnet&#xff0c;Building Automation and Control networks的简称&#xff0c;即楼宇自动化与控制网络。是用于智能建筑的通信协议。 一般楼宇自控设备从功能上讲分为两部分&#xff1a;一部分专门处理设备的控制功能&#xff1b;另一部分专…

设计模式之【代理模式】,有事找我“经纪人”

文章目录 一、什么是代理模式1、代理模式三大角色2、代理、桥接、装饰器、适配器 4 种设计模式的区别3、代理模式使用场景4、代理模式优缺点 二、静态代理1、静态代理的一般写法2、火车站售票案例3、静态代理优缺点 三、动态代理1、静态代理和动态代理的本质区别2、JDK动态代理…

ArcSWAT报错:Error Number :-2147467259; 对 COM 组件的调用返回了错误 HRESULT E_FAIL

文章目录 1 报错内容2 报错解决3 并行处理的设置补充说明 1 报错内容 通常为连续两段报错&#xff1a; Error Number :-2147467259 Error Message :对 COM 组件的调用返回了错误 HRESULT E_FAIL 。 Module name : mSWFlow Function name : createStream Procedure ( error li…

星辰天合参加首届数字驱动创新峰会 强调以 SDS 加速数据基础设施建设

5 月 11 日&#xff0c;2023 数字驱动创新峰会在北京新世纪日航饭店隆重举办。作为赛迪网、《数字经济》杂志社首次主办的数字驱动峰会&#xff0c;本届峰会以“新要素、新生产、新经济”为主题&#xff0c;下设数字金融创新论坛、数字制造创新论坛和数字服务创新论坛三个分论坛…

4 月 NFT 月报:在动荡的 NFT 市场中寻求生存

作者&#xff1a;lesleyfootprint.network 数据来源&#xff1a;Footprint NFT Research 上个月&#xff0c;NFT市场在 4 月 5 日出现了交易量高峰&#xff0c;随后交易量又在月底大幅下降了 50%。近期&#xff0c;NFT 卖家的数量持续超过买家的数量&#xff0c;这表明市场可…

4面华为测试开发,居然挂在这个地方....

说一下我面试别人时候的思路 反过来理解&#xff0c;就是面试时候应该注意哪些东西&#xff1b;用加粗部分标注了 一般面试分为这么几个部分&#xff1a; 一、自我介绍 这部分一般人喜欢讲很多&#xff0c;其实没必要。大约5分钟内说清楚自己的职业经历&#xff0c;自己的核…