[Código fuente de Android R] Análisis del código fuente del mecanismo de procesamiento de mensajes de Hnadler

1. Métodos de uso común de Handler

Handler es un mecanismo de mensajes proporcionado por Android. Por lo general, se usa para aceptar los datos enviados por el subproceso secundario y usar estos datos para cooperar con el subproceso principal para actualizar la interfaz de usuario.

Por ejemplo, hacer clic en un botón va al servidor para solicitar datos. Si la operación de solicitud se realiza directamente en el subproceso principal (subproceso de interfaz de usuario), la interfaz aparecerá como una animación suspendida y, si no se ha completado durante mucho tiempo, se recibirá un mensaje de error "La aplicación no responde (ANR)" el sistema Android. ¿por qué? Debido a que en Android, la capacidad de respuesta de la aplicación es monitoreada por los servicios del sistema Administrador de actividades y Administrador de ventanas. Por lo general, el cuadro de diálogo ANR aparecerá en las siguientes tres situaciones :

1: KeyDispatchTimeout (el valor predeterminado de Google es 5 s, la plataforma MTK es 8 s): la tecla de tipo principal o el evento táctil no responde dentro de un tiempo específico

2: BroadcastTimeout(10s) BroadcastReceiver no puede completar el procesamiento dentro de un tiempo específico

3: ServiceTimeout (20 s): el servicio de tipo de probabilidad pequeña no se puede procesar dentro de un tiempo específico.

Hay muchas formas en que el controlador puede enviar mensajes

  • msj.sendToTarget();
  • mHandler.sendMessageAtFrontOfQueue();
  • mHandler.sendEmptyMessage()
  • mHandler.sendEmptyMessageDelayed()
  • mHandler.post()
  • mHandler.postDelayed()

Luego colocamos estas operaciones que consumen mucho tiempo en el subproceso secundario para que se ejecuten. Pero aquí viene el problema, los datos deben completarse en los controles relacionados para su visualización. Sin embargo, la actualización de la interfaz de usuario en Android solo se puede actualizar en el subproceso principal, y las operaciones en los subprocesos son peligrosas, por lo que debe usar un controlador.

// 创建一个Handler 对象,运行在主线程 ui线程中
    private final Handler myHandler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case 0:
                    String reslut = (String) msg.obj;
                    textView.setText(reslut);
            }
        }
    };

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            textView = findViewById(R.id.myText);
            button = findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    threadRun();
                }
            });
        }


        @Override
        protected void onResume() {
            super.onResume();
        }

// 创建个 子线程
        private void threadRun() {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        for (int i = 0; i < 100; i++) {
                            result += i;
                        }
// 休眠个 6 s
                        Thread.sleep(6000);
                        Message message = Message.obtain();
                        message.what = 0;
                        message.obj = result;

// 通过handler将子线程数据传给主线程
                        myHandler.sendMessage(message);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
        }

2. Clases importantes de mecanismos de procesamiento de mensajes

  • ThreadLocal

ThreadLocal es una variable local del subproceso, que se mantiene individualmente por cada subproceso. Cuando use ThreadLocal para mantener variables, proporcione una copia independiente de la variable para cada subproceso que use la variable, es decir, cada subproceso tendrá una variable dentro, de modo que varios subprocesos que accedan a la variable al mismo tiempo no se afecten entre sí. por lo que se utilizan Todos son copias de variables copiadas de la memoria, por lo que no hay problema de seguridad de subprocesos.

  • Mensaje 

Message implementa la interfaz Parcelable, es decir, realiza la serialización , lo que indica que Message se puede usar para la comunicación entre procesos.   

  • Cola de mensajes

MessageQueue es de hecho un contenedor para almacenar mensajes (objetos de mensajes). Es una lista vinculada unidireccional . El objeto de mensaje tiene un campo siguiente para guardar el siguiente en la lista, y mMessages en MessageQueue guarda el primer elemento de la lista vinculada. .

  • Looper 

Looper circulator, Looper es para proporcionar un bucle de mensajes para hilos. De manera predeterminada, el subproceso no tiene un Looper, puede usar prepare() para obtener un bucle y usar el método loop() para comenzar a procesar información hasta que el bucle se detenga.
La mayor parte de la interacción con el bucle de información se realiza a través de la clase Handler.

Un MessageQueue necesita un Looper. 
  • Manipulador 

controlador controlador

Responsable del envío y procesamiento del Mensaje. Al usar Handler, debe implementar el método handleMessage(Message msg) para procesar un mensaje específico y guardarlo en la cola de mensajes, como actualizar la interfaz de usuario. 

Mecanismo de controlador de Android_Controlador de Android

 En combinación con el ejemplo de código anterior y el proceso de implementación de la figura anterior, para usar Handler para implementar el procesamiento de mensajes asincrónicos, primero debemos crear un objeto Handler en el subproceso principal y reescribir el método handleMessage(), y luego, cuando las operaciones de la interfaz de usuario estén requerido en el subproceso, cree un objeto Message y envíe este mensaje a través de Handlerr. Después de eso, el mensaje se agregará a la cola MessageQueue para ser procesado, y Looper siempre intentará sacar el mensaje pendiente de MessageQueue y finalmente distribuirlo de nuevo al método handleMessage() del controlador. Dado que Halldler se crea en el subproceso principal, el código del método handleMessage() también se ejecutará en el subproceso principal en este momento, de modo que el subproceso secundario pueda realizar el propósito de la operación del subproceso de la interfaz de usuario a través del mecanismo del controlador.
 

3. Análisis del código fuente

Primer vistazo a la creación del hilo principal:

ActivityThread es lo que a menudo llamamos hilo principal o hilo de interfaz de usuario. El método principal de ActivityThread es el punto de entrada real de una aplicación, y MainLooper se crea en su método principal. Tanto Handler como Looper se crean e inicializan en esta etapa

  • frameworks/base/core/java/android/app/ActivityThread.java
    public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // Install selective syscall interception
        AndroidOs.install();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        // Call per-process mainline module initialization.
        initializeMainlineModules();

        Process.setArgV0("<pre-initialized>");

// 开始主线程循环
        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
// 创建 thread 对象
        ActivityThread thread = new ActivityThread();
// 在attach方法中会完成Application对象的初始化,然后调用Application的onCreate()方法
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
// 获取到 Handler 对象
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// 循环
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }


--------------------------------------
其中:获取到 Handler 对象
    final H mH = new H();

    @UnsupportedAppUsage
    public Handler getHandler() {
        return mH;
    }

    class H extends Handler {
        public static final int BIND_APPLICATION        = 110;

        public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
。。。。。。
// 创建服务
                case CREATE_SERVICE:
                    if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
                        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                                ("serviceCreate: " + String.valueOf(msg.obj)));
                    }
                    handleCreateService((CreateServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
// 绑定服务
                case BIND_SERVICE:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceBind");
                    handleBindService((BindServiceData)msg.obj);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;


El controlador del subproceso principal, como variable miembro de ActivityThread, se inicializa cuando se ejecuta el método principal de ActivityThread y se crea ActivityThread. MessageQueue se inicializa y crea como una variable miembro cuando se crea Looper.

1) El controlador  sendMessage envía un mensaje a la cola de mensajes

Si el subproceso secundario desea comunicarse con el subproceso ui y enviar un mensaje, inicie el análisis desde myHandler.sendMessage(mensaje):

  • frameworks/base/core/java/android/os/Handler.java
    public final boolean sendMessage(@NonNull Message msg) {
// 延时为 0
        return sendMessageDelayed(msg, 0);
    }

    public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {

// 先获取到消息队列
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
// 最终调用到消息队列去 入队列
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Llame a enqueueMessage() para enviar el mensaje a MessageQueue. Entonces, ¿qué es MessageQueue? Como su nombre lo indica, es una cola de mensajes. De hecho, cuando creamos un controlador, debe asociarse con un Looper. La clase Looper tiene una variable miembro

Eche un vistazo para obtener la cola de mensajes MessageQueue queue = mQueue, ¿de dónde viene mQueue?

Se le asigna un valor en el constructor de Handler

    public Handler(@Nullable Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
// 这里通过 Looper 获取到 looper 对象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
// 通过 looper 获取到 消息队列
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

// 第二个构造方法,直接传入 looper
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

El método enqueueMessage de MessageQueue entra en la cola

  • frameworks/base/core/java/android/os/MessageQueue.java
    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }

        synchronized (this) {
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }

            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
// 如果队列前面没有元素p == null,如果没有延时,时间为0,则马上执行,则直接入队列首位(链表实现),等待loop的轮询
// 排在队列第一位的先执行
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
// 遍历所有的message对象
                for (;;) {
                    prev = p;
                    p = p.next;
// 
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

Si no se cumple ninguna de las condiciones anteriores, ingrese else.Podemos ver que la línea 17 tiene un bucle for para recorrer los objetos de mensaje existentes, y la línea 20 tiene una instrucción if when < p.when when when es un mensaje nuevo.Tiempo de ejecución, p.cuándo es el tiempo de ejecución del mensaje mensaje en la cola Si se encuentra un mensaje que se ejecuta más tarde que el nuevo mensaje, se ejecuta la operación de inserción en la lista enlazada:

mensaje.siguiente = p; 

anterior.siguiente = mensaje;

Es decir, insértelo delante del mensaje y ejecute primero el nuevo mensaje.

2) Crear un objeto Looper

En el frente, puede ver que cuando se crea el subproceso principal, se llamará al método prepareMainLooper para crear un método looper, por lo que siempre habrá un objeto Looper en el subproceso principal de la aplicación, por lo que no es necesario hacerlo manualmente. llama al método Looper.prepare() Crea un looper en el hilo principal.

  • frameworks/base/core/java/android/os/Looper.java
    @UnsupportedAppUsage
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    public static void prepareMainLooper() {
// 在prepare 会创建个对象 Looper
// bool 参数是用于判断是否可以 退出循环,主线程为 false,不退出
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

// 创建对象 Looper,保存到 sThreadLocal中
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));

// 通过 sThreadLocal获取对应创建的 Looper 
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }    

El campo sThreadLocal se modifica por static final, lo que indica que es una constante compartida por la clase, que proporciona una copia de la clase Looper para cada hilo.

¿Cómo asegurarse de que solo haya un Looper en un hilo? El constructor de Looper es privado y solo se puede llamar en preparación. Y si un ThreadLocal obtiene el valor correspondiente, significa que ya se ha creado. lanzará una excepción. En su constructor, cada objeto looper vincula el hilo actual con una cola de mensajes

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
// 获取当前线程
        mThread = Thread.currentThread();
    }

 Una forma más estándar de escribir un hilo de procesamiento de mensajes asíncrono:

class LooperThread extends Thread {
      public Handler mHandler;
 
      public void run() {
          Looper.prepare();
 
          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };
 
          Looper.loop();
      }
  }

// 在子线程中发送消息

    public void click(View v){
    final Handler handler =  new Handler();
    new Thread(new Runnable() {
        @Override
        public void run() {
            Looper.prepare();
            handler.sendMessage();
            Looper.loop();
        }
    }) .start();
}

3) Empezar a trabajar con Looper

Es llamar al método loop(): sondear para obtener el mensaje Mensaje

  • frameworks/base/core/java/android/os/Looper.java
    public static void loop() {

// 获取 Looper 对象
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        if (me.mInLoop) {
            Slog.w(TAG, "Loop again would have the queued messages be executed"
                    + " before this one completed.");
        }

        me.mInLoop = true;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        me.mSlowDeliveryDetected = false;

// 
        for (;;) {
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }

método loopOnce:

    @SuppressWarnings("AndroidFrameworkBinderIdentity")
    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
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what);
        }
        // Make sure the observer won't change while processing a transaction.
        final Observer observer = sObserver;

        final long traceTag = me.mTraceTag;
        long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
        if (thresholdOverride > 0) {
            slowDispatchThresholdMs = thresholdOverride;
            slowDeliveryThresholdMs = thresholdOverride;
        }
        final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
        final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

        final boolean needStartTime = logSlowDelivery || logSlowDispatch;
        final boolean needEndTime = logSlowDispatch;

        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }

        final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
        final long dispatchEnd;
        Object token = null;
        if (observer != null) {
            token = observer.messageDispatchStarting();
        }
        long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
        try {
// 将消息分发,发送给 自己定义的子类
            msg.target.dispatchMessage(msg);
            if (observer != null) {
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } catch (Exception exception) {
            if (observer != null) {
                observer.dispatchingThrewException(token, msg, exception);
            }
            throw exception;
        } finally {
            ThreadLocalWorkSource.restore(origWorkSource);
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        if (logSlowDelivery) {
            if (me.mSlowDeliveryDetected) {
                if ((dispatchStart - msg.when) <= 10) {
                    Slog.w(TAG, "Drained");
                    me.mSlowDeliveryDetected = false;
                }
            } else {
                if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                        msg)) {
                    // Once we write a slow delivery log, suppress until the queue drains.
                    me.mSlowDeliveryDetected = true;
                }
            }
        }
        if (logSlowDispatch) {
            showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
        }

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

Un mecanismo de comunicación entre procesos de Linux: pipe. Principio: hay un archivo especial en la memoria, este archivo tiene dos identificadores (referencias), uno es un identificador de lectura y el otro es un identificador de escritura

El subproceso principal Looper lee los mensajes de la cola de mensajes. Cuando se leen todos los mensajes, entra en suspensión y el subproceso principal se bloquea. El subproceso envía un mensaje a la cola de mensajes y escribe datos en el archivo de canalización. El subproceso principal se activa para leer datos del archivo de canalización. El subproceso principal se activa solo para leer el mensaje. Cuando se lee el mensaje , vuelve a dormir. Por lo tanto, no consumirá demasiado rendimiento.

Leer datos de MessageQueue

  • frameworks/base/core/java/android/os/MessageQueue.java
    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
// 获取当前时间
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;

// 查找是否有异步消息在队列中
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
// 此处处理延时任务
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
// 取出对应的 message
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

nextPollTimeoutMillis es un parámetro muy importante. Durante el bucle normal, este parámetro es 0. Cuando hay un mensaje retrasado y el mensaje no está listo, se establecerá un tiempo de retraso para que se ejecute el siguiente ciclo. Cuando el mensaje está vacío, este parámetro se establecerá en -1.

El método nativePollOnce es un método nativo y también es un método para bloquear la cola de mensajes. Cuando el parámetro anterior es -1, la cola de mensajes entrará en estado de espera.

Flujo del mecanismo de procesamiento de mensajes del controlador:

1. Si desea que Handler funcione normalmente, debe tener un objeto Looper en el hilo actual

2. Cuando se inicializa el objeto Looper, se creará un MessageQueue asociado con él;

3.MessageQueue almacena y administra mensajes

4. Mensaje es el objeto de mensaje recibido y procesado por el controlador

Por lo tanto, el procesamiento del controlador del código fuente de Android se divide en dos procesos principales: aceptación y procesamiento:

1. Enviar un mensaje: envíe un mensaje a través de publicación, envío y otros métodos (esencialmente enviar) para enviar un mensaje (ejecutable también se convierte en un mensaje al final). Al final, se llama al método enqueueMessage de MessageQueue para insertar el mensaje en MessageQueue

2. Recibir mensajes: debemos habilitar Looper.loop() para sondear, y eliminar y obtener el objeto Mensaje enviado previamente llamando al método next() en MessageQueue (cuando MessageQueue está vacío, sigue los bloques). El objeto Message obtenido finalmente es procesado por el método dispatchMessage del objeto Handler (message.target) que envió el mensaje. El método dispatchMessage eventualmente irá al método handlerMessage(). Entonces, cuando creamos un controlador, podemos configurarlo en su handlerMessage () o devolución de llamada

4) Preguntas comunes de la entrevista

1.  ¿Por qué Looper.loop() en el hilo principal siempre se repite infinitamente sin causar ANR ?

Porque cuando el Looper termine de procesar todos los mensajes, entrará en estado de bloqueo, y cuando entre un nuevo Mensaje, romperá el bloqueo y continuará ejecutándose.

Esto en realidad no entiende el concepto de ANR. ANR, nombre completo La aplicación no responde. Cuando envío un mensaje de dibujo de la interfaz de usuario al controlador del subproceso principal y no se ejecuta después de un cierto período de tiempo, se genera una excepción ANR. El bucle infinito de Looper es para realizar varias transacciones en un bucle, incluidas las transacciones de dibujo de la interfaz de usuario. El bucle infinito del Looper indica que el subproceso no está muerto. Si el Looper deja de hacer un bucle, el subproceso termina y sale. El bucle infinito de Looper en sí mismo es una de las razones para garantizar que las tareas de dibujo de la interfaz de usuario se puedan ejecutar. Al mismo tiempo, la tarea de dibujo de la interfaz de usuario tiene una barrera de sincronización, que puede garantizar una ejecución más rápida del dibujo.

2. ¿Por qué el subproceso principal no inicializa Looper?

Porque la aplicación ha inicializado el subproceso principal Looper durante el proceso de inicio.

Cada aplicación java tiene una entrada de método principal, y Android es un programa basado en Java no es una excepción. La entrada del programa Android está en el método principal de ActivityThread:

public static void main(String[] args) {
    ...
 // 初始化主线程Looper
    Looper.prepareMainLooper();
    ...
    // 新建一个ActivityThread对象
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    // 获取ActivityThread的Handler,也是他的内部类H
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    ...
    Looper.loop();
 // 如果loop方法结束则抛出异常,程序结束
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

En el método principal, primero inicialice el subproceso principal Looper, cree un nuevo objeto ActivityThread y luego inicie Looper, de modo que el subproceso principal Looper se ejecute cuando se inicie el programa. Ya no necesitamos inicializar el hilo principal Looper.

3. ¿Cómo garantiza Handler la seguridad del acceso simultáneo a MessageQueue?

Bloqueo cíclico, con mecanismo despertador de bloqueo.

Podemos encontrar que MessageQueue es en realidad un modelo de "productor-consumidor". El controlador sigue introduciendo mensajes y Looper sigue sacándolos, lo que implica problemas de interbloqueo. Si el Looper obtiene el bloqueo, pero no hay ningún mensaje en la cola, esperará para siempre, y el controlador debe ingresar el mensaje, pero el Looper retiene el bloqueo y no puede unirse a la cola, lo que provoca un interbloqueo. La solución al mecanismo Handler es el bloqueo circular . En el siguiente método de MessageQueue:

Message next() {
   ...
    for (;;) {
  ...
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            ...
        }
    }
}

Podemos ver que su espera está fuera de la cerradura. Cuando no hay ningún mensaje en la cola, primero liberará la cerradura y luego esperará hasta que se despierte. Esto no causará problemas de punto muerto.

Luego, al ingresar a la cola, ¿mantendrá el bloqueo mientras espera que se procese el mensaje porque la cola está llena? La diferencia es que MessageQueue no tiene un límite superior para los mensajes, o su límite superior es la memoria asignada por la JVM al programa.Si la memoria excede el límite, se lanzará una excepción, pero generalmente no lo hará.

4. ¿Cómo cambia Handler los hilos?

Use Looper de diferentes hilos para procesar mensajes.

El subproceso de ejecución del código no está determinado por el código en sí, sino en qué subproceso se encuentra la lógica de ejecución de este código, o qué lógica de subproceso se llama. Cada Looper se ejecuta en el subproceso correspondiente, por lo que el método dispatchMessage llamado por diferentes Looper se ejecuta en el subproceso donde se encuentra.

5. ¿Cuál es el mecanismo de activación de bloqueo de Handler?

Respuesta: El mecanismo de activación de bloqueo de Handler se basa en el mecanismo de activación de bloqueo de Linux.

Este mecanismo también es un patrón similar al mecanismo del controlador. Cree un descriptor de archivo localmente, y luego la parte que debe esperar escucha este descriptor de archivo, y la parte que se despierta solo necesita modificar el archivo, luego la parte que está esperando recibirá el archivo y interrumpirá la activación. Es similar a Looper escuchando MessageQueue y Handler agregando mensajes.

6. Resuelva el método de pérdida de memoria del controlador

El método de escritura del controlador de la clase interna anónima, la clase interna contiene la clase externa, que es la causa de la actividad, lo que provoca una pérdida de memoria.

La idea más directa es evitar el uso de clases internas no estáticas. Cuando use Handler, colóquelo en un archivo nuevo para heredar Handler o use una clase interna estática en su lugar. Las clases internas estáticas no contienen implícitamente referencias a clases externas, por lo que no habrá pérdidas de memoria en esta actividad.

Dos soluciones son las siguientes:

  1. Referencia débil (WeakReference)

  public class SampleActivity extends Activity {

  /**
   * Instances of static inner classes do not hold an implicit
   * reference to their outer class.
   * 弱引用的方式
   */
    private static class MyHandler extends Handler {
    private final WeakReference<SampleActivity> mActivity;
    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference<SampleActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
      SampleActivity activity = mActivity.get();
      if (activity != null) {
         //to Something
      }
    }
 }

2. Usar clase estática interna

  //定义成static的,因为静态内部类不会持有外部类的引用
  private final MyHandler mHandler = new MyHandler(this);
  private static final Runnable sRunnable = new Runnable() {
      @Override
      public void run() {//to something}
  };

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
    finish();
  }
}

De hecho, la mayoría de las fugas de memoria en Android son causadas por el uso de clases internas no estáticas por parte de Activity, por lo que debemos prestar especial atención al usar clases internas. Si las referencias que contiene exceden el alcance del ciclo de vida, es muy probablemente habrá una fuga de memoria.

referencia:

Análisis integral de la entrevista del mecanismo Handler

Exploración y principio del mecanismo del controlador

mecanismo del manipulador

Supongo que te gusta

Origin blog.csdn.net/qq_40587575/article/details/121283409
Recomendado
Clasificación