Supervise las operaciones del subproceso principal que consumen mucho tiempo y resuelva los ANR desde el desarrollo

antecedentes:

En el entorno de depuración , el tiempo de ejecución del cuerpo del método en el subproceso principal se compara con el tiempo especificado, y se compara la información de la pila, y el cuerpo del método que consume mucho tiempo y excede el tiempo especificado se optimiza de manera específica para reducir la aparición de ANR .

Esta clase de herramienta imprime principalmente la información de la pila que consume mucho tiempo y el tiempo que consume mucho tiempo que excede el tiempo especificado en el subproceso principal Looper, en el que el tiempo de verificación se define por sí mismo, y verifica activamente las operaciones que consumen mucho tiempo en el subproceso principal para prevenir problemas antes de que ocurran.

principio:

Esta clase de herramienta es la herramienta de procesamiento más simple y directa para optimizar las operaciones que consumen mucho tiempo. Todos conocen los criterios de juicio de Android para ANR:

La oración más simple es: ANR: la aplicación no responde, la actividad es de 5 segundos, BroadCastReceiver es de 10 segundos, el servicio es de 20 segundos

Entonces, la solución de esta clase de herramienta es comparar y monitorear la información de la pila del subproceso principal por tiempo e imprimir el tiempo de espera.

Análisis Looper.loop:

  1. La razón por la que la aplicación no se cierra es porque se está ejecutando en el bucle. Si hay una operación que bloquea el bucle, se producirá ANR y se bloqueará.
public static void loop() {
    final Looper me = myLooper();
    //....
    for (;;) {
        if (!loopOnce(me, ident, thresholdOverride)) {
            return;
        }
    }
}
  1. Mire principalmente el bucle infinito loopOnce
private static boolean loopOnce(final Looper me,
        final long ident, final int thresholdOverride) {
    Message msg = me.mQueue.next(); // might block
    if (msg == null) {
        // No message indicates that the message queue is quitting.
        return false;
    }

    // This must be in a local variable, in case a UI event sets the logger
    // *当有任务的时候打印Dispatching to *
    final Printer logging = me.mLogging;
    if (logging != null) {
        logging.println(">>>>> Dispatching to " + msg.target + " "
                + msg.callback + ": " + msg.what);
    }
    //.... 中间部分未任务执行的代码
    
    //执行结束之后打印 Finished to 
    if (logging != null) {
        logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
    }

    // Make sure that during the course of dispatching the
    // identity of the thread wasn't corrupted.
    final long newIdent = Binder.clearCallingIdentity();
    if (ident != newIdent) {
        Log.wtf(TAG, "Thread identity changed from 0x"
                + Long.toHexString(ident) + " to 0x"
                + Long.toHexString(newIdent) + " while dispatching to "
                + msg.target.getClass().getName() + " "
                + msg.callback + " what=" + msg.what);
    }

    msg.recycleUnchecked();

    return true;
}
  1. El tiempo que consume entre los comentarios anteriores es el tiempo que consume el subproceso principal al ejecutar una determinada tarea. Podemos monitorear la información de la pila del subproceso principal que consume mucho tiempo comparando este tiempo con el tiempo especificado.

Cómo utilizar:

  1. Solicitud:
 //主线程中方法体执行的时间与指定的时间做对比后的堆栈信息,针对性的优化超过指定时间的耗时方法体,
MainThreadDoctor.init(500)
  1. Ver registro:

imagen.png

Los niveles de registro utilizan el nivel de error para mayor claridad.

Instrumentos:

 /**
*  @author  kong
*  @date  2022/7/6 15:55
*  @description  在debug环境中主线程中方法体执行的时间与指定的时间做对比后的堆栈信息,针对性的优化超过指定时间的耗时方法体,减少ANR的发生
**/
object MainThreadDoctor {

    private  var startTime = 0L
    private  var currentJob: Job? = null
 private  const  val START = ">>>>> Dispatching"
    private  const  val END = "<<<<< Finished"

    fun init(diagnoseStandardTime: Long) {
        if (BuildConfigs.DEBUG) {
            diagnoseFromMainThread(diagnoseStandardTime)
        }
    }

    /**
*  @param diagnoseStandardTime 执行诊断的标准时间
*/
 fun diagnoseFromMainThread(diagnoseStandardTime: Long) {
        Looper.getMainLooper().setMessageLogging {
 if (it.startsWith(START)) {
                startTime = System.currentTimeMillis()
                currentJob = GlobalScope.launch(Dispatchers.IO) {
delay(diagnoseStandardTime)
                    val stackTrace = Looper.getMainLooper().thread.stackTrace
 val builder = StringBuilder()
                    for (s in stackTrace) {
                        builder.append(s.toString())
                        builder.append("\n")
                    }
                    PPLog.e("looperMessageMain $builder")
                }
}

            if (it.startsWith(END)) {
                if (currentJob?.isCompleted == false) {
                    currentJob?.cancel()
                } else {
                    PPLog.e("looperMessageMain 总时间 = ${System.currentTimeMillis() - startTime} 毫秒")
                }
            }
        }
}
}

Supongo que te gusta

Origin juejin.im/post/7117194640826368036
Recomendado
Clasificación