Un artículo para obtener "ANR en Android"

¿Qué es ANR?

Es el monitoreo del tiempo de espera del sistema a través de los componentes que interactúan y la interacción del usuario para determinar si el proceso de la aplicación está atascado o responde con demasiada lentitud. En términos generales, es la idea de diseño del perro guardián en muchos sistemas.

Da un ejemplo para ayudarte a entender ANR

Pregunta: ¿Se producirá ANR al llamar al método de suspensión en el método onCreate de la actividad?
La mayoría de las personas pueden pensar que ANR ocurrirá siempre que se realicen operaciones que consumen mucho tiempo en el subproceso principal, entonces, ¿es este realmente el caso?
¿Llamar a Thread.sleep (20 * 1000) en el método onCreate de la actividad para dejar que el hilo principal duerma durante 20 segundos hará que la aplicación ANR? Escriba una demostración para probarlo.

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();
        }
    }
}

Como el código anterior, ejecute el programa, el resultado es que no se produce ANR en la aplicación y el registro se imprime normalmente después de dormir durante 60 segundos.
inserte la descripción de la imagen aquí
¡Míralo! ¡Míralo! Acuéstese durante 20 segundos y luego continúe.
Vamos a ejecutarlo de nuevo, con una diferencia con respecto a la última vez. Estamos en el sueño 20, desencadenando un evento de retorno.
inserte la descripción de la imagen aquí
¡Míralo! ¡Míralo! ¡ANR se estrelló!
Hermanos: A partir de los resultados anteriores, sabemos que, de hecho, en el método de suspensión del subproceso principal o al realizar operaciones que consumen mucho tiempo, es posible que no se genere necesariamente ANR. La causa raíz de ANR es que la aplicación no puede responder a las operaciones del usuario dentro de un
cierto período de tiempo. Porque el hilo principal está ocupado. Los hermanos inteligentes ya deberían saber por qué, sí, es el Handler And Looper de nuestro hilo principal. No hay forma de procesar el siguiente mensaje.

La causa de la ANR

  • Tiempo de espera del servicio: por ejemplo, si el servicio en primer plano no se ejecuta en 20 s, el tiempo de espera del servicio en segundo plano es 10 veces mayor que el del servicio en primer plano, 200 s;
  • Tiempo de espera de BroadcastQueue: por ejemplo, la transmisión en primer plano no se ejecuta en 10 segundos y la transmisión en segundo plano es de 60 segundos.
  • Tiempo de espera de ContentProvider: proveedor de contenido, tiempo de espera 10 segundos después de la publicación;
  • Tiempo de espera de envío de entrada: el tiempo de espera de envío del evento de entrada es de 5 s, incluidos los eventos táctiles y de botón.
//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;

Medios de monitoreo ANR

Método 1: monitorear la carpeta de seguimiento

El problema de permisos solo se aplica a
las versiones de Android M (6.0) anteriores a Android 6, y el lado de la aplicación no puede monitorear directamente si se produce ANR al monitorear el archivo de datos/anr/rastreo.

Método 2: Usa el Looper de nuestro hilo principal

Echemos un vistazo a nuestro proceso 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);
        }
    }
    ...
}

Los hermanos inteligentes ya lo han pensado: podemos usar las anotaciones 2 y 4 para obtener el tiempo que procesamos el mensaje. Para juzgar nuestra situación ANR.

Solo necesitamos llamar a Looper.getMainLooper().setMessageLogging(printer) para obtener el tiempo antes y después de que Handler procese un mensaje de la devolución de llamada.

Debe tenerse en cuenta que dispatchMessage ya se llamó y se eliminó de la pila después de que se detectó la tartamudez. En este momento, la pila del subproceso principal no contiene el código de tartamudeo.

Por lo tanto, es necesario abrir un subproceso en segundo plano, obtener la pila del subproceso principal con regularidad, usar el punto de tiempo como clave y la información de la pila como valor, y guardarlo en el Mapa. recuperar la información de la pila durante el período de congelación.

Sin embargo, esta solución solo es adecuada para uso sin conexión por las siguientes razones:

  1. logging.println("<<<<< Terminado en " + msg.target + " " + msg.callback); Hay una concatenación de cadenas, las llamadas frecuentes crearán una gran cantidad de objetos, lo que provocará inestabilidad en la memoria.
  2. El subproceso de fondo obtiene con frecuencia la pila de subprocesos principal, lo que tiene cierto impacto en el rendimiento, y obtener la pila de subprocesos principal suspenderá el funcionamiento del subproceso principal.

Método 3: monitorear la señal SIGQUIT

Este tipo de esquema es el ANR de monitoreo real, que es utilizado por matrix y xCrash. Se ha probado en aplicaciones como la aplicación nacional WeChat y se puede garantizar la estabilidad y la confiabilidad.

El proceso ANR se completa básicamente en el proceso del sistema system_server. Es difícil para nosotros monitorear el comportamiento del proceso del sistema. Si queremos monitorear este asunto, tenemos que comenzar desde el límite entre el proceso del sistema y el proceso de la aplicación para ver si hay algún lugar operable en el límite.
Ahora, resolvamos todo el proceso ANR:

  1. Después de que el sistema monitorea que ANR ocurre en la aplicación, recopila algunos pid de procesos relacionados (incluido el proceso en el que ocurre ANR) y se prepara para volcar la pila de estos procesos para generar un archivo de seguimiento de ANR.
  2. El sistema comienza a enviar la señal SIGQUIT a estos procesos, y el proceso comienza a volcar la pila después de recibir la señal SIGQUIT. El
    equipo de WeChat analiza el diagrama de flujo de ANR:
    inserte la descripción de la imagen aquí
    puede ver que en todo el proceso después de que ANR ocurre en un proceso , solo se producirá el comportamiento de la pila de volcado cuando se produzca ANR. En el proceso, todos los demás procesos se procesan en el proceso del sistema y no podemos percibirlo. Este proceso comienza con la recepción de la señal SIGQUIT y finaliza con el uso de socket para escribir Trace. Luego continúe regresando al proceso del sistema para completar el proceso ANR restante.Podemos hacer un escándalo sobre estos dos límites.

Rastros de registro ANR.txt

Primero, el archivo traces.txt se encuentra en el directorio /data/anr/, puede copiarlo en el directorio de la tarjeta SD para verlo mediante el siguiente comando adb.

El análisis de archivos de seguimiento para localizar ANR se aplica principalmente sin conexión, por supuesto, podemos poner nuestro archivo traces.txt en línea. Pero necesitamos hacer esto:
1. Cuando el subproceso de monitoreo encuentra que el subproceso principal está atascado, envía activamente una señal SIGNAL_QUIT al sistema.
2. Espere a que se genere el archivo /data/anr/traces.txt.
3. Envíe el archivo después de generarlo.

Parece factible, pero hay dos problemas:
1. Traces.txt contiene toda la información del subproceso, que debe filtrarse y analizarse manualmente después de la carga
2. Muchos sistemas de versiones altas requieren privilegios de root para leer /data/anr directorio
inserte la descripción de la imagen aquí

Análisis de archivos de trazas

Obtenga el archivo de rastreo y analícelo en detalle:

----- 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个线程

Interpretación detallada del parámetro de traza:

"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 Ejecutable

  • Demonio "Signal Catcher": nombre del subproceso, demonio significa subproceso de demonio
  • prio: prioridad del hilo
  • tid: identificación interna del subproceso
  • Estado del hilo: Ejecutable
    inserte la descripción de la imagen aquí

En términos generales: el subproceso principal está en estado de BLOQUEO, ESPERANDO y TIMEWAITING, que es básicamente ANR causado por el bloqueo de funciones. Si el subproceso principal es normal, debe verificar la carga de la CPU y el entorno de la memoria.

Varios casos de análisis:

1. El problema del buen posicionamiento (caso sencillo)

Primero vea el registro de 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
......

Desde la primera línea del registro, puede ver el nombre del paquete de la aplicación y el nombre de la clase donde ocurrió el error, aquí está

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

Luego vea que el número de proceso PID es 7398. El motivo de ANR es que se agotó el tiempo de envío de entrada, que es el primero mencionado anteriormente. Más abajo está el registro de uso de la CPU de los procesos activos.

Solo mirando los registros en Logcat solo puedo ver esta información. Probablemente sé que hay un problema en MainActivity, pero todavía no puedo ubicar claramente la línea de código donde ocurrió ANR. Si desea obtener más información sobre el error, puede solo puede verlo generado durante el proceso ANR. El archivo de información de la pila traces.txt está activo.
Información en 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)
......
......

Comencemos desde el principio y veamos el significado de cada campo:


Nombre del subproceso: prioridad del subproceso principal : prio=5
ID de bloqueo del subproceso: tid=1
Estado del subproceso: Nombre del grupo de subprocesos nativos
: group=“main”
Número de veces que se suspende el subproceso: sCount=1
Número de veces que se suspende el subproceso por el depurador: dsCount=0
subproceso dirección del objeto java: obj=0x75ceafb8
subproceso mismo Dirección del objeto nativo: self=0x55933ae7e0
######################### ## ########
Información de programación de subprocesos:
ID de subproceso del núcleo en el sistema Linux: sysTid=7398 es el mismo que el número de proceso del subproceso principal
Prioridad de programación de subprocesos: nice=0
Grupo de programación de subprocesos: cgrp=
subproceso predeterminado política de programación y prioridad: sched=0/0
dirección de la función de procesamiento de subprocesos: handle=0x7f7ddae0f0
############################### ##### ##
Información de contexto del subproceso:
estado de programación del subproceso: estado = S
tiempo de ejecución del subproceso en la CPU, tiempo de espera del subproceso, longitud del segmento de tiempo de ejecución del subproceso: schedstat=(1014853999443411372871 31344)
valor del tiempo de programación del subproceso en modo de usuario: utm = 9936
El valor de tiempo de programación del subproceso en el estado del kernel: stm=212
El número de serie del núcleo de la CPU que finalmente ejecuta este subproceso: core=1
######################################
Información de la pila de subprocesos:
dirección y tamaño de la pila: pila = 0x7fc8d40000-0x7fc8d42000 tamaño de pila = 8 MB

Finalmente vea esta línea en la información de la pila:

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

Aquí podemos ver claramente que el problema ocurrió en la línea 887 de AttendanceCheckInFragment, y luego es fácil encontrar la causa de ANR en la línea de código correspondiente.

Dos, mal posicionamiento

El hilo principal está bloqueado por un candado.

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)

......

Fíjate en las siguientes líneas:

"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)  

El tid del subproceso principal es 1, el estado del subproceso es Bloqueado y está esperando el Objeto 0x0c6f8c52, que está en manos del subproceso 22, y el subproceso principal actualmente tiene el bloqueo de 0x01abeb23. Y el tid del canal es 22, que también está en estado Bloqueado. El candado que quiere solicitar y el candado existente están justo enfrente del hilo principal. En este caso, se encuentra el motivo de ANR: el subproceso 22 mantiene un bloqueo y no lo ha liberado, y el subproceso principal espera a que se agote el tiempo de espera del bloqueo. En el entorno en línea, el escenario común de ANR debido a bloqueos es la escritura de SharePreference.

La CPU está adelantada

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
  ......

Se puede ver que el proceso ocupa hasta el 543% de la CPU, ocupando la mayor parte de los recursos de la CPU, porque provoca ANR, que no tiene nada que ver con nuestra app.

La tensión de la memoria causa ANR

Si la CPU y la pila de un registro ANR son normales, se puede considerar que la memoria es escasa. Mire la parte relacionada con la memoria del registro ANR. También puede buscar onTrimMemory en el registro. Si hay registros relacionados cerca del momento de volcar el registro ANR, es posible que la memoria esté apretada.

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

El tiempo de espera del servicio del sistema provoca 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)

En la pila de registros, se puede ver que se produjo ANR para obtener información de la red: getActiveNetworkInfo. Los servicios del sistema son todos mecanismos Binder (16 subprocesos), y las capacidades del servicio también son limitadas.Es posible que los servicios del sistema no respondan durante mucho tiempo y provoquen ANR. Si otras aplicaciones ocupan todos los subprocesos de Binder, la aplicación actual solo puede esperar. Puede buscar más: palabra clave blockUntilThreadAvailable:

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

Si la pila de un subproceso determinado contiene esta palabra, puede seguir mirando su pila para determinar a qué servicio del sistema se llama. Este tipo de ANR también es un problema del entorno del sistema.Si este problema ocurre con frecuencia en un determinado tipo de teléfono móvil, la capa de aplicación puede considerar una estrategia de evitación.

Resumir

La mayoría de los estudiantes pueden centrarse en las necesidades comerciales y no prestar mucha atención a los problemas de ANR, o depender directamente de terceros, como Bugly, pero, en la entrevista, el entrevistador básicamente no le preguntará sobre el uso de estas herramientas. . Así que vamos a aprenderlo.

Supongo que te gusta

Origin blog.csdn.net/weixin_45112340/article/details/130690447
Recomendado
Clasificación