Inventario del clásico de Handler decenas de preguntas consecutivas

Autor: Ah, él

1. Hable sobre la comprensión de Handler.

//\frameworks\base\core\java\android\os\Handler.java
//\frameworks\base\core\java\android\os\Looper.java
//\frameworks\base\core\java\android\os\Message.java
//\frameworks\base\core\java\android\os\MessageQueue.java
  1. Android Handler es un conjunto de mecanismos de mensajería asincrónica (mecanismo de comunicación asincrónica). Se aplica principalmente a la transferencia de información entre diferentes subprocesos en el mismo componente (o en el mismo archivo).

  2. A veces es necesario realizar operaciones IO que consumen mucho tiempo en subprocesos, que pueden ser leer archivos o acceder a la red, etc. Una vez completadas las operaciones que consumen mucho tiempo, es posible que se requieran algunos cambios en la interfaz de usuario. De acuerdo con la especificación de desarrollo de Android, no podemos acceder al control de la interfaz de usuario en el hilo secundario, de lo contrario se activará una excepción del programa. En este momento, la operación de actualización de la interfaz de usuario se puede cambiar al hilo principal a través del controlador.

  3. El mecanismo Handler consta de cuatro componentes: Handler, Message, MessageQueue y Looper.

El mensaje es la información que se pasa entre subprocesos, puede transportar una pequeña cantidad de información internamente y se utiliza para intercambiar datos entre diferentes subprocesos;

Handler, como su nombre indica, significa procesador y se utiliza principalmente para enviar y procesar mensajes. Generalmente se utiliza el envío de mensajes Handler.sendMessage()y, después de una serie de procesamientos, los mensajes enviados eventualmente se entregarán a Handler.handleMessage();

MessageQueue significa cola de mensajes, que se utiliza principalmente para almacenar todos los mensajes enviados a través de Handler. Esta parte del mensaje siempre existirá en la cola de mensajes, esperando ser procesada, que es esencialmente una lista vinculada unidireccional ordenada por tiempo;

Looper es el administrador de MessageQueue en cada hilo. Después de llamar al método loop () de Looper, ingresará a un bucle infinito y luego, cada vez que se encuentre un mensaje en MessageQueue, se extraerá y se pasará al método Handler.handleMessage().

  1. Cómo utilizar el controlador
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  class LooperThread extends Thread {
  *      public Handler mHandler;
  *
  *      public void run() {
  *          Looper.prepare();
  *
  *          mHandler = new Handler(Looper.myLooper()) {
  *              public void handleMessage(Message msg) {
  *                  // process incoming messages here
  *              }
  *          };
  *
  *          Looper.loop();
  *      }
  *  }</pre>
  */

Mi propia comprensión del uso del mecanismo Handler:

R. Looper.prepare()La esencia es crear un Looper, y la esencia central de crear un Looper es crear un MessageQueue. Looper puede considerarse como el motor de la tubería y MessageQueue es la bandeja en el cinturón de la tubería;

public static void prepare() {
    prepare(true);
}
​
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) );
}
​
private Looper(boolean quitAllowed) {
    mQueue =  new MessageQueue(quitAllowed) ;
    mThread = Thread.currentThread();
}

b.Es new Handler(Looper.myLooper())decirle al Manejador (trabajador) qué línea de ensamblaje manejar, Handler.sendMessage()es decir, dejar que el Manejador (trabajador) coloque las piezas (mensaje) a procesar en la bandeja en la correa de la línea de ensamblaje y Handler.dispatchMessage()coloque las piezas. en la bandeja al final de la línea de montaje (mensaje) se saca para su procesamiento,
c. Looper.loop()Es para iniciar el motor de la tubería Looper para hacer que la tubería funcione;


a. Las piezas (Mensaje) en la cinta transportadora son las primeras en entrar, primeras en salir. Cada vez que un trabajador coloca una pieza (Mensaje) en el palé de la cinta transportadora (MessageQueue), considerará la prioridad de qué pieza debe procesarse y la colocará en la cinta transportadora MessageQueue de acuerdo con el orden de prioridad que debe procesarse. estar procesado;

b. Cada vez que el trabajador (Handler) termine de procesar las piezas (mensaje), recogerá las paletas y las arrojará a la pila de paletas (sPool), esta pila de paletas puede contener hasta 50, y luego cuando los trabajadores necesiten poner piezas y use la plataforma, simplemente tómela directamente desde aquí, ahorrando tiempo;

c. Cuando las piezas (mensaje) en la bandeja de la correa de la línea de montaje no hayan llegado al tiempo de procesamiento, el Looper detendrá la transmisión y entrará en el estado de espera;

d. Una tubería completa tiene solo un motor (Looper) y una cinta transportadora (MessageQueue), mientras que puede haber varios trabajadores (Handler). Cuando un trabajador coloca una pieza (Mensaje) en la cinta transportadora, agregará la información personal del trabajador (Manejador) (msg. Worker handle(dispatchMessage(msg));

e. Cuando no hay ninguna pieza (Mensaje) en la cinta transportadora (MessageQueue), el motor (Looper) ya no funciona y entra en estado de espera, y todo el sistema se bloquea en el estado de preparación para tomar la siguiente pieza (MessageQueue). .next()), de hecho, llame al método subyacente de NativePollOnce. Una vez que haya piezas nuevas (Mensaje) en la cinta transportadora, el sistema de tuberías continuará ejecutándose;

2. ¿Cuántos Loopers tiene un hilo? ¿Cuántas colas de mensajes hay? ¿Cómo garantizar?

Un hilo tiene solo un objeto Looper y un objeto MessageQueue.

Antes de crear el controlador, es necesario llamarlo Looper.prepare(). Esta función garantiza que cada hilo tenga solo un objeto Looper.

/** Initialize the current thread as a looper.
  * This gives you a chance to create handlers that then reference
  * this looper, before actually starting the loop. Be sure to call
  * {@link #loop()} after calling this method, and end it by calling
  * {@link #quit()}.
  */
public static void prepare() {
    prepare(true);
}
​
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));
}

MessageQueue se crea en el constructor Looper, lo que garantiza que un Looper corresponda a un MessageQueue.

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

3. ¿Cuántos controladores puede tener un hilo? Entonces, ¿cómo garantizar que el mensaje se pueda asignar al controlador correcto para su procesamiento?

Debido a que el Controlador puede ser nuevo en la Actividad, también puede ser nuevo en el Servicio y todas las Actividades se ejecutan en el hilo principal, lo que demuestra que puede haber varios Controladores en el hilo principal.

Cuando el controlador esté dentro sendMessageAtTime(), se completará solomsg.target

public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
    boolean sent = false;
    MessageQueue queue = mQueue;
    if (queue != null) {
         msg.target = this; 
        sent = queue.enqueueMessage(msg, uptimeMillis);
    }
    else {
        RuntimeException e = new RuntimeException(
            this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
    }
    return sent;
}

Luego, Looper.loop()cuando obtenga continuamente el procesamiento de mensajes de MessageQueue, llamará al mensaje de despacho correspondiente de acuerdo con msg.target, donde msg.target es el controlador anterior.

public static final void loop() {
    Looper me = myLooper();
    MessageQueue queue = me.mQueue;
    while (true) {
        Message msg = queue.next(); // might block
        if (msg != null) {
            if (msg.target == null) {
                return;
            }
            if (me.mLogging!= null) me.mLogging.println(
                    ">>>>> Dispatching to " + msg.target + " "
                    + msg.callback + ": " + msg.what
                    );
             msg.target.dispatchMessage(msg); 
            if (me.mLogging!= null) me.mLogging.println(
                    "<<<<< Finished to    " + msg.target + " "
                    + msg.callback);
            msg.recycle();
        }
    }
}

4. ¿Cuál es la estructura de datos de MessageQueue? Varios subprocesos agregan datos a MessageQueue, ¿cómo garantizar la seguridad de los subprocesos?

MessageQueue es una estructura de datos de primero en entrar, primero en salir. La implementación subyacente es una lista vinculada unidireccional ordenada por tiempo. Cuando un mensaje ingresa a la cola, se ordena e inserta de acuerdo con el valor de cuándo del mensaje.

Los bloqueos sincronizados se utilizan para MessageQueue.enqueueMessage()garantizar la seguridad de los hilos.MessageQueue.next()

final boolean enqueueMessage(Message msg, long when) {
    if (msg.when != 0) {
        throw new AndroidRuntimeException(msg + " This message is already in use.");
    }
    if (msg.target == null && !mQuitAllowed) {
        throw new RuntimeException("Main thread not allowed to quit");
    }
      synchronized (this) { 
        if (mQuiting) {
            RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");
            Log.w("MessageQueue", e.getMessage(), e);
            return false;
        } else if (msg.target == null) {
            mQuiting = true;
        }
        msg.when = when;
        Message p = mMessages;
         if (p == null || when == 0 || when < p.when) {
            msg.next = p;
            mMessages = msg;
            this.notify();
        } else {
            Message prev = null;
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
            msg.next = prev.next;
            prev.next = msg; 
            this.notify();
        }
    }
    return true;
}
​
Message next() {
    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 {
                    // 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;
            }
        ...
    }
}

5. ¿Por qué el hilo principal puede ser un nuevo Handler? ¿Qué trabajo debe hacer el subproceso secundario si quiere un nuevo controlador?

Looper.prepareMainLooper()Debido a que y se han agregado de antemano en el hilo principal Looper.loop(), si el hilo secundario quiere llamar al nuevo controlador, debe llamar Looper.prepare()yLooper.loop()

//\frameworks\base\core\java\android\app\ActivityThread.java

public static void main(String[] args) {
    ...
     Looper.prepareMainLooper(); 

    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()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

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

     Looper.loop(); 
}

csharp

public static void prepareMainLooper() {
     prepare(false) ;
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

6. ¿Es precisa la hora del mensaje de Handler.postDelayed()? ¿Principio de implementación?

De hecho, este problema es el mismo que el de la seguridad del subproceso: una vez que el subproceso es seguro en subprocesos múltiples, la hora no puede ser precisa y una vez que la hora es precisa, el subproceso no debe ser seguro. Porque cuando varios subprocesos acceden a la cola, se bloquearán al ponerlos en la cola y sacar mensajes. Cuando el primer subproceso no ha completado el acceso, el segundo subproceso no se puede utilizar, por lo que su tiempo real será diferente. Está retrasado. . Por lo tanto, la hora del mensaje retrasado enviado por el controlador es básicamente precisa, pero no completamente exacta.

Principio de implementación : postDelayedFinalmente, se llamará a uno con un parámetro de retraso sendMessageAtTime, y luego MessageQueue.enqueueMessageel mensaje con un parámetro de tiempo de retraso se insertará en MessageQueue en orden de tiempo. MessageQueue es una lista enlazada unidireccional ordenada por tiempo. Cuando Looper recupera el mensaje de MessageQueue, juzgará si la hora actual ha alcanzado el tiempo de retraso del primer mensaje al principio de la lista enlazada. Si aún no ha llegado, se calculará comparando el tiempo de retraso con el tiempo actual. Descubra el tiempo de espera y luego nativePollOncerealice una espera de bloqueo a través de la función nativa hasta que se acabe el tiempo de espera y luego active el hilo para ejecutar el mensaje;

7. ¿Qué sucede cuando no hay ningún mensaje en MessageQueue? ¿Por qué Looper.loop no bloquea el hilo principal?

Cuando la cola MessageQueue está vacía, Looper.loop()el bucle infinito no saldrá ni se ejecutará, pero se bloqueará en MessageQueue.next()el método nativePollOnce()y entrará en un estado inactivo, esperando que lleguen nuevos mensajes y se active nuevamente. Esto implicará la implementación del mecanismo de canalización y epoll del Linux subyacente.

8. ¿Por qué el bucle infinito del Handler no se atasca?

El ANR atascado en la aplicación no tiene nada que ver con el bucle infinito del Looper . Cuando la aplicación no tiene ningún mensaje para procesar, está inactiva y el hilo se libera; y ANR significa que el mensaje no se ha procesado a tiempo, como el botón y el evento táctil no se procesan en 5 segundos, o la transmisión en primer plano no se procesado en 10 segundos, etc., lo que resulta en Atascado;

9. ¿IdleHandler lo entiende?

IdelHandler es una interfaz interna estática en MessageQueue. Cuando el mensaje obtenido por Looper de MessageQueue está vacío, o cuando el tiempo de ejecución no ha terminado, es decir, cuando MessageQueue está inactivo, volverá a llamar IdleHandler.queueIdle().

Si queueIdle()devuelve falso, el controlador inactivo se eliminará después de la ejecución, es decir, se ejecutará solo una vez, si devuelve verdadero, se mantendrá y la próxima vez que MessageQueue entre en estado inactivo para continuar con la ejecución;

//\frameworks\base\core\java\android\os\MessageQueue.java

/**
 * Callback interface for discovering when a thread is going to block
 * waiting for more messages.
 */
public static interface IdleHandler {
  /**
   * Called when the message queue has run out of messages and will now
   * wait for more.  Return true to keep your idle handler active, false
   * to have it removed.  This may be called if there are still messages
   * pending in the queue, but they are all scheduled to be dispatched
   * after the current time.
   */
  boolean queueIdle();
}

Message next() {
    ......
    for (;;) {
        ......
        synchronized (this) {
        // 此处为正常消息队列的处理
            ......
            if (mQuitting) {
                dispose();
                return null;
            }
            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)];
            }
            //mIdleHandlers 数组,赋值给 mPendingIdleHandlers
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        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);
                }
            }
        }
        pendingIdleHandlerCount = 0;
        nextPollTimeoutMillis = 0;
    }
}

Ejemplo de uso en el código fuente del sistema : ActivityThread.handleResumeActivity()en el medio, después de ejecutar el método onResume, se llama Looper.myQueue().addIdleHandler(new Idler())para realizar algunas tareas menos urgentes, como la recuperación de recursos y la impresión de registros. Además, IdleHandler también se puede utilizar al optimizar el rendimiento del proyecto , que ejecuta tareas cuando el hilo principal está inactivo sin afectar la ejecución de otras tareas.

@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    // If we are getting ready to gc after going to the background, well
    // we are back active so skip it.
    unscheduleGcIdler();
    mSomeActivitiesChanged = true;
        ···
        //该方法最终会执行 onResume方法
    final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
    if (r == null) {
        // We didn't actually resume the activity, so skipping any follow-up actions.
        return;
    }
        ··· 
        ···
    r.nextIdle = mNewActivities;
    mNewActivities = r;
    if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);
     Looper.myQueue().addIdleHandler(new Idler()); 
}

private class Idler implements MessageQueue.IdleHandler {
    @Override
    public final boolean queueIdle() {
        ActivityClientRecord a = mNewActivities;
            ···
        if (a != null) {
            mNewActivities = null;
            IActivityManager am = ActivityManager.getService();
            ActivityClientRecord prev;
            do {
                //打印一些日志
                if (localLOGV) Slog.v(
                    TAG, "Reporting idle of " + a +
                    " finished=" +
                    (a.activity != null && a.activity.mFinished));
                if (a.activity != null && !a.activity.mFinished) {
                    try {
                        //AMS 进行一些资源的回收
                        am.activityIdle(a.token, a.createdConfig, stopProfiling);
                        a.createdConfig = null;
                    } catch (RemoteException ex) {
                        throw ex.rethrowFromSystemServer();
                    }
                }
                prev = a;
                a = a.nextIdle;
                prev.nextIdle = null;
            } while (a != null);
        }
        if (stopProfiling) {
            mProfiler.stopProfiling();
        }
        
        //确认Jit 可以使用,否则抛出异常
        ensureJitEnabled();
        return false;
    }
}

10. ¿Entiendes la barrera de la sincronización?

Se puede entender que la barrera de sincronización es una estrategia de prioridad proporcionada por el mecanismo de mensajes del controlador , que puede mejorar la prioridad de los mensajes asincrónicos .

Hay tres tipos de mensajes en el mecanismo del controlador: mensajes sincrónicos, mensajes asincrónicos y mensajes de barrera. Los mensajes que usamos normalmente son todos mensajes sincrónicos. Los mensajes asincrónicos se pueden configurar cuando se construye el controlador o se pueden configurar a través de la configuración. La diferencia entre mensajes de barrera y mensajes sincrónicos setAsynchronouses que la propiedad de destino es nula . Cuando Looper recupera mensajes de MessageQueue, si no se encuentra ningún mensaje de barrera, el mensaje sincrónico y el mensaje asincrónico son los mismos. Si se encuentra un mensaje de barrera, todos los mensajes sincrónicos posteriores al mensaje se bloquearán y solo se ejecutarán los mensajes asincrónicos.

En el proceso de dibujo de la interfaz de usuario , se utilizan barreras sincrónicas y mensajes asincrónicos para garantizar que cuando llegue la señal Vsync, las tareas asincrónicas se puedan procesar primero, de modo que las tareas de dibujo se puedan ejecutar a tiempo para evitar la congelación de la interfaz.

@UnsupportedAppUsage
Message next() {
    for (;;) {
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            Message msg = mMessages;
             //如果msg.target为空,也就是说是一个同步屏障消息,则进入这个判断里面
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                //在这个while循环中,找到最近的一个异步消息
                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 {
                    // 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;
            }
            ...
}

Además, debe tenerse en cuenta que la barrera de sincronización no se eliminará automáticamente y debe eliminarse manualmente después de su uso; de lo contrario, el mensaje de sincronización no se procesará. Es decir, como se mencionó anteriormente, a través removeSyncBarrier(int token)del método para eliminar, el token es el token devuelto cuando se agregó la barrera antes.

public void removeSyncBarrier(int token){
}

11. ¿Por qué el Handler pierde memoria? ¿Cómo resolver?

razón

El mensaje del controlador se almacena en MessageQueue. Algunos mensajes no se pueden procesar de inmediato y existirán en MessageQueue durante mucho tiempo, lo que hará que el controlador no se pueda reciclar. Debido a que Handler es una instancia de una clase interna anónima no estática, implícitamente mantendrá la actividad de clase externa, de modo que la actividad no se pueda reciclar, lo que provocará que la actividad pierda memoria.

Solución

  1. Use un controlador estático modificado y use una referencia débil al objeto de actividad, porque necesita usar los miembros en el objeto de actividad (debido a que la clase interna estática no contiene referencias a la clase externa, por lo que usar un controlador estático no causará la actividad de fuga);
  2. Defina el controlador por separado y también haga referencia débil a la actividad;
  3. Utilice el controlador de la clase interna para eliminarCallbacksAndMessages en el método onDestroy;
  4. Otro método es utilizar directamente la biblioteca de código abierto Handler WeakHandler para evitar pérdidas de memoria;

12. ¿Cómo crear un Mensaje? ¿Por qué?

Para crear un mensaje, se recomienda utilizar Message.obtain() en lugar de new message(). Debido a que la clase Mensaje mantiene un objeto sPool, puede entenderse como una lista vinculada de mensajes y la longitud máxima predeterminada de esta lista vinculada es 50. En el mecanismo de mensajes de Android, cada vez que se procesa un objeto Mensaje, se colocará en este grupo, lo que nos permitirá reutilizarlo. Cuando llamamos Message.obtain()a un método, no creamos un nuevo objeto Mensaje si reutilizamos el objeto Mensaje que existe en el grupo. Esto evita la sobrecarga de rendimiento causada por la frecuente creación y destrucción de objetos Message . Reduzca la fluctuación de la memoria y el OOM.

  /** Constructor (but the preferred way to get a Message is to call {@link #obtain() Message.obtain()}).
  */
  public Message() {
  }
  
   public static Message obtain() { 
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

13. Hilo del controlador

Handlerthread es una clase heredada de Thread. Se usa para abrir un nuevo hilo con un looper. Puede usar este looper para crear un Handler. Cabe señalar que se debe llamar al método para abrir el hilo start(). Porque start()puede llamar run()al método del hilo, y HandlerThread.run()llamará Looper.prepare()y Looper.loop()vinculará el Looper para el hilo secundario, lo que equivale a una capa de encapsulación, lo cual es conveniente para que los usuarios usen el Handler.

//frameworks\base\core\java\android\os\HandlerThread.java

/*
Handy class for starting a new thread that has a looper.
The looper can then be used to create handler classes.
Note that start() must still be called.
*/

public class HandlerThread extends Thread {
  ...

  @Override
  public void run() {
      mTid = Process.myTid();
      Looper.prepare();// HandlerThread在start()的时候执行run()方法,而Looper就是在这里被创建的
      synchronized (this) {
          mLooper = Looper.myLooper();
          notifyAll();
      }
      Process.setThreadPriority(mPriority);
      onLooperPrepared();
      Looper.loop();
      mTid = -1;
  }
}

Cómo utilizar Handler y HandlerThread juntos

HandlerThread thread1 = new HandlerThread("test1");
thread1.start();

Handler mHandler = new Handler(thread1.getLooper()){
  @Override
  public void handleMessage(Message msg) {
      switch (msg.what) {
          case DownloadThread.TYPE_START:
              Log.e(TAG, "4.主线程知道Download线程开始下载了...这时候可以更改主界面UI");
              break;
          default:
              break;
      }
      super.handleMessage(msg);
  }
}

14. Servicio de intención

IntentService es una clase básica heredada de Service y es esencialmente un Servicio. Se utiliza principalmente para responder a solicitudes asincrónicas basadas en intenciones. El cliente startService(Intent)envía una solicitud a través de IntentService, que se inicia después de recibir la solicitud y procesa las solicitudes de Intent asincrónicas secuencialmente en el subproceso recién creado, y solo se procesará una solicitud al mismo tiempo. Cuando se completan todas las solicitudes, IntentService se cierra solo.

¿Por qué se proporciona IntentService después de que se proporciona el Servicio oficial?

El servicio se ejecuta en el subproceso principal de forma predeterminada. Si necesitamos procesar algunas tareas que consumen mucho tiempo en el Servicio, entonces debemos crear subprocesos manualmente o usar grupos de subprocesos para procesar las tareas que consumen mucho tiempo (de lo contrario, se producirá ANR) y luego, después del procesamiento. En el futuro, cierre manualmente el Servicio e IntentService ya ha hecho este trabajo por nosotros. Solo necesitamos escribir onHandleIntentel código de la tarea que requiere mucho tiempo en él y luego se puede ejecutar en el subproceso, onHandleIntentporque se ejecuta en el subproceso y en la tarea Después de la ejecución, IntentService ejecutará stopSelf(startId)el método por sí solo y se cerrará.

¿Cuáles son los beneficios de utilizar IntentService?

En primer lugar, nos ahorramos la molestia de abrir manualmente subprocesos en el Servicio y, en segundo lugar, no necesitamos detener manualmente el Servicio cuando se completa la operación.

Para ver un ejemplo del uso de ServiceIntent, consulte el código fuente; el efecto de ejecución es el siguiente:

//\frameworks\base\core\java\android\app\IntentService.java

/**
 * IntentService is an extension of the {@link Service} component class that
 * handles asynchronous requests (expressed as {@link Intent}s) on demand.
 * Clients send requests
 * through {@link android.content.Context#startService(Intent)} calls; the
 * service is started as needed, handles each Intent in turn using a worker
 * thread, and stops itself when it runs out of work.
 */
@Deprecated
public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    @UnsupportedAppUsage
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }
    ...

    @Override
    public void onCreate() {
        super.onCreate();
        //这边就使用到了 HandlerThread 的工具类
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        // mServiceHandler 本质是 Handler,使用了 mServiceLooper 去创建
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    /**
     * This method is invoked on the worker thread with a request to process.
     * Only one Intent is processed at a time, but the processing happens on a
     * worker thread that runs independently from other application logic.
     * So, if this code takes a long time, it will hold up other requests to
     * the same IntentService, but it will not hold up anything else.
     * When all requests have been handled, the IntentService stops itself,
     * so you should not call {@link #stopSelf}.
     *
     * @param intent The value passed to {@link
     *               android.content.Context#startService(Intent)}.
     *               This may be null if the service is being restarted after
     *               its process has gone away; see
     *               {@link android.app.Service#onStartCommand}
     *               for details.
     */
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}
  • El mecanismo epoll de Linux.

    • El mecanismo epoll es un mecanismo de multiplexación de IO que puede monitorear múltiples descriptores al mismo tiempo. Cuando un descriptor está listo (listo para leer o escribir), notifica inmediatamente al programa correspondiente para realizar una operación de lectura o escritura, esencialmente E / S sincrónica. es decir, se bloquea la lectura y la escritura. Por lo tanto, el hilo principal está inactivo la mayor parte del tiempo y no consume muchos recursos de la CPU.
/**
 * A Handler allows you to send and process Message and Runnable
 * objects associated with a thread's MessageQueue. Each Handler
 * instance is associated with a single thread and that thread's message
 * queue. When you create a new Handler it is bound to a Looper.
 * It will deliver messages and runnables to that Looper's message
 * queue and execute them on that Looper's thread.
 *
 * <p>There are two main uses for a Handler: (1) to schedule messages and
 * runnables to be executed at some point in the future; and (2) to enqueue
 * an action to be performed on a different thread than your own.
 *
 * <p>Scheduling messages is accomplished with the
 * {@link #post}, {@link #postAtTime(Runnable, long)},
 * {@link #postDelayed}, {@link #sendEmptyMessage},
 * {@link #sendMessage}, {@link #sendMessageAtTime}, and
 * {@link #sendMessageDelayed} methods.  The <em>post</em> versions allow
 * you to enqueue Runnable objects to be called by the message queue when
 * they are received; the <em>sendMessage</em> versions allow you to enqueue
 * a {@link Message} object containing a bundle of data that will be
 * processed by the Handler's {@link #handleMessage} method (requiring that
 * you implement a subclass of Handler).
 * 
 * <p>When posting or sending to a Handler, you can either
 * allow the item to be processed as soon as the message queue is ready
 * to do so, or specify a delay before it gets processed or absolute time for
 * it to be processed.  The latter two allow you to implement timeouts,
 * ticks, and other timing-based behavior.
 * 
 * <p>When a
 * process is created for your application, its main thread is dedicated to
 * running a message queue that takes care of managing the top-level
 * application objects (activities, broadcast receivers, etc) and any windows
 * they create.  You can create your own threads, and communicate back with
 * the main application thread through a Handler.  This is done by calling
 * the same <em>post</em> or <em>sendMessage</em> methods as before, but from
 * your new thread.  The given Runnable or Message will then be scheduled
 * in the Handler's message queue and processed when appropriate.
 */
public class Handler {

}

Si aún no comprende completamente Handler y desea comprenderlo en el menor tiempo posible, puede consultar "Puntos de conocimiento básicos del marco de Android" , que incluye: Init, Zygote, SystemServer, Binder, Handler, AMS, PMS, Launcher. .. y otros registros de puntos de conocimiento.

"Manual de resumen de los puntos de conocimiento básicos del marco" :https://qr18.cn/AQpN4J

Parte del principio de implementación del mecanismo Handler:
1. Análisis macroteórico y análisis del código fuente del mensaje
2. Análisis del código fuente de MessageQueue
3. Análisis del código fuente del Looper
4. Análisis del código fuente del Handler
5. Resumen

Principio de Binder:
1. Puntos de conocimiento que deben comprenderse antes de aprender Binder
2. Mecanismo de Binder en ServiceManager
3. Proceso de registro de servicios del sistema
4. Proceso de inicio de ServiceManager
5. Proceso de adquisición de servicios del sistema
6. Inicialización de Java Binder
7. Java El proceso de registro del sistema servicios en carpeta

Cigoto:

  1. El proceso de inicio del sistema Android y el proceso de inicio de Zygote
  2. El proceso de inicio del proceso de solicitud.

Análisis del código fuente de AMS:

  1. Gestión del ciclo de vida de la actividad.
  2. Proceso de ejecución de onActivityResult
  3. Explicación detallada de la gestión de la pila de actividades en AMS

Código fuente de PMS en profundidad:

1. Proceso de inicio y proceso de ejecución de PMS
2. Análisis del código fuente de instalación y desinstalación de APK
3. Estructura de coincidencia de filtro de intención en PMS

WMS:
1. El nacimiento de WMS
2. Los miembros importantes de WMS y el proceso de adición de Window
3. El proceso de eliminación de Window

"Manual de estudio del marco de Android":https://qr18.cn/AQpN4J

  1. Proceso de inicio de arranque
  2. Inicie el proceso Zygote en el arranque
  3. Inicie el proceso SystemServer al arrancar
  4. conductor de carpeta
  5. Proceso de inicio de AMS
  6. El proceso de inicio del PMS
  7. Proceso de inicio del lanzador
  8. Los cuatro componentes principales de Android
  9. Servicio del sistema Android: proceso de distribución del evento de entrada
  10. Análisis del código fuente del mecanismo de actualización de la pantalla de renderizado subyacente de Android
  11. Análisis del código fuente de Android en la práctica

Supongo que te gusta

Origin blog.csdn.net/weixin_61845324/article/details/132623649
Recomendado
Clasificación