Análisis del código fuente de Android: patrón de diseño de peso ligero, mecanismo de paso de mensajes del controlador (basado en el análisis del SDK API 33 de Android)

Análisis de código fuente de Android: patrón de diseño de peso ligero, mecanismo de paso de mensajes del controlador (basado en el análisis del SDK API 33 de Android)

1. Definición

Utilice objetos compartidos para admitir de manera eficiente una gran cantidad de objetos detallados

Núcleo: Reutilización de objetos.

1.1 Demostración del modo Flyweight

Demostración de compra de billetes de tren

//火车票
public class Ticket {
    
    
    private String from;
    private String to;

    public Ticket(String from, String to) {
    
    
        this.from = from;
        this.to = to;
    }

    public int getPrice() {
    
    
        return new Random().nextInt(100) + 20;
    }
}

Caché de objetos en un mapa. A continuación también analizaremos

//火车票查询工厂
public class TicketFactory {
    
    
    public static Map<String, Ticket> sTicketMap = new HashMap<>();

    public static Ticket getTicket(String from, String to) {
    
    
        String key = from + "-" + to + "";
        Ticket ticket = sTicketMap.get(key);
        if (ticket != null) {
    
    
            return ticket;
        }
        ticket = new Ticket(from, to);
        sTicketMap.put(key, ticket);
        return ticket;
    }
}

2. Análisis de ejemplos de código fuente en Android Message

uso

val obtain = Message.obtain()

Seguir a lo largo

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
    
    
        //防止多线程并发
        synchronized (sPoolSync) {
    
    
            //从线程池取对象
            if (sPool != null) {
    
    
                //链表取出每个Message对象
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

Este es el patrón de diseño de peso mosca más obvio.

3. Controlador asociado al mensaje

Un punto de conocimiento en el desarrollo de Android: la interfaz de usuario no se puede actualizar en subprocesos secundarios.

class DebugActivity : AppCompatActivity() {
    
    
    private val TAG = javaClass.simpleName
    private var handler: Handler = Handler(Looper.getMainLooper())
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
    fun doSomething(){
    
    
        thread {
    
    
            //耗时操作,得到结果,不能在这个线程更新 UI
            // Handler 将结果传递到主线程中,更新UI
            handler.post {
    
    
                //更新UI
            }
        }
    }
}

Seguimos la función de publicación.

 public final boolean post(@NonNull Runnable r) {
    
    
    
       return  sendMessageDelayed(getPostMessage(r), 0);
 }
 private static Message getPostMessage(Runnable r) {
    
    
        Message m = Message.obtain();
        m.callback = r;
        return m;
  } 

El controlador pasa un Runnable al subproceso de la interfaz de usuario y lo instala en un objeto de mensaje.

Seguimiento de la función sendMessageDelayed

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) {
    
    
     //当前 Handler 所在的消息队列
        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);
 }

La función sendMessageDelayed llama a la función sendMessageAtTime. Sin pasar manualmente el Looper, el Looper retenido por el Handler es el Looper del hilo actual, es decir, el Handler se crea en qué hilo, es el Looper de ese hilo.

El objeto Mensaje en getPostMessage es la función Message.obtain()

 Message m = Message.obtain();

Analiza este código,

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
    
    
        //防止多线程并发
        synchronized (sPoolSync) {
    
    
            //从线程池取对象
            if (sPool != null) {
    
    
                //链表取出每个Message对象
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

El grupo de mensajes Message no utiliza un contenedor como un mapa, sino una lista vinculada.

Insertar descripción de la imagen aquí

¿Cómo ponerlo en este grupo de mensajes?

Vamos a ver

El objeto Mensaje se recicla en el grupo de mensajes.

public void recycle() {
    
    
    //该消息还在使用
        if (isInUse()) {
    
    
            if (gCheckRecycle) {
    
    
                throw new IllegalStateException("This message cannot be recycled because it "
                        + "is still in use.");
            }
            return;
        }
    //消息添加到消息池中
        recycleUnchecked();
    }

Continúe con recycleUnchecked()

 void recycleUnchecked() {
    
    
        // Mark the message as in use while it remains in the recycled object pool.
        // Clear out all other details.
     //清空消息状态
     flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;
		//回收消息到消息池中
        synchronized (sPoolSync) {
    
    
            if (sPoolSize < MAX_POOL_SIZE) {
    
    
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

Aquí la lista vinculada se utiliza como un grupo de búfer para almacenar objetos de mensajes. Cada vez que se genera un mensaje, se agrega a la lista vinculada.

4. Mecanismo de mensajes de Android

La entrada a la aplicación de Android es en realidad ActivityThread, síguela

public static void main(String[] args) {
    
    
        ......
		//创建Looper,UI线程的消息队列
        Looper.prepareMainLooper();
		......
    	//启动应用程序
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
		//循环消息
        Looper.loop();

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

Looper recupera mensajes de la cola de mensajes y los procesa. El controlador agrega continuamente mensajes a la cola de mensajes y los mensajes se procesan continuamente.

Entonces, ¿cómo se asocia el controlador con la cola de mensajes?

Constructor de controlador

public Handler(@Nullable Callback callback, boolean async) {
    
    
      ......
        mLooper = Looper.myLooper();//获取 Looper
 	 ......
        mQueue = mLooper.mQueue;//获取消息队列
        mCallback = callback;
        mAsynchronous = async;
    }

Handler obtiene el objeto Looper a través de myLooper(),

Continúe con myLooper()

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

El objeto Looper se almacena en sThreadLocal,

	@Deprecated
    public static void prepareMainLooper() {
    
    
        prepare(false);
        synchronized (Looper.class) {
    
    
            if (sMainLooper != null) {
    
    
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
//prepare()方法中创建了一个 Looper 对象
	private static void prepare(boolean quitAllowed) {
    
    
        if (sThreadLocal.get() != null) {
    
    
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //将该对象设置给了sThreadLocal,这样线程和队列就关联上了
        sThreadLocal.set(new Looper(quitAllowed));
    }

El controlador está asociado con el hilo y la cola de mensajes del hilo, y los mensajes enviados por el controlador se ejecutarán en este hilo.

Llame a la función de bucle del Looper para recuperar y procesar continuamente mensajes de la cola de mensajes.

 public static void loop() {
    
    
      ......
          //死循环
        for (;;) {
    
    
            //取消息
            if (!loopOnce(me, ident, thresholdOverride)) {
    
    
                return;
            }
        }
    }

Seguimiento con loopOnce

private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
    
    
    //获取消息 (might block )
        Message msg = me.mQueue.next(); // might block
       ......
        try {
    
    
            //处理消息
            msg.target.dispatchMessage(msg);
            if (observer != null) {
    
    
                observer.messageDispatched(token, msg);
            }
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } 
    	......
            //回收消息,也就是我们分析享元模式时提到的将 Message 添加到消息池的操作
        msg.recycleUnchecked();

        return true;
    }

Eche un vistazo al código central de next()

Message next() {
    
    
       		......
			//native层的事件
            nativePollOnce(ptr, nextPollTimeoutMillis);
				......
                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;
                }

              ......
        }
    }

La siguiente función extrae mensajes de la cola de mensajes en secuencia. Si el mensaje alcanza el tiempo de ejecución, el mensaje se devuelve al Looper y el puntero de la lista de la cola se mueve hacia atrás.

5. La creación de un controlador en un subproceso secundario genera una excepción

class DebugActivity : AppCompatActivity() {
    
    
    private val TAG = javaClass.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        thread {
    
    
            val handler = Handler()
        }
    }
 
}

Insertar descripción de la imagen aquí

Análisis: el objeto Looper es ThreadLocal y cada hilo tiene su propio Looper. Al crear un objeto Handler en un hilo secundario, si el Looper está vacío, se generará una excepción. Seguimiento del método de construcción del Handler.

public Handler(@Nullable Callback callback, boolean async) {
    
    
        ......
        //获取looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
    
    
            //抛出异常
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
     ......
    }

El objeto mLooper está vacío y se genera una excepción. El objeto Looper en este hilo no se ha creado. El objeto Looper de este hilo está vacío antes de que se llame manualmente a Looper.prepare en el hilo secundario. La solución es configurar el objeto Looper para el hilo actual antes de construir el controlador.

class DebugActivity : AppCompatActivity() {
    
    
    private val TAG = javaClass.simpleName
    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        thread {
    
    
            //绑定到 ThreadLocal中
            Looper.prepare()
            val handler = Handler()
            //启动消息循环
            Looper.loop()
        }
    }

}

这样子线程的Looper对象就不会为null了,有了自己的消息队列。

Supongo que te gusta

Origin blog.csdn.net/weixin_46039528/article/details/132391529
Recomendado
Clasificación