Princípio do manipulador de uma pessoa, um gato viajar

Se algumas operações demoradas forem realizadas no encadeamento principal (também chamado de encadeamento de UI), ocorrerão problemas de ANR. Para evitar ANR, é necessário abrir um sub-thread para processar operações demoradas, como solicitações de rede, operações de banco de dados e operações de leitura de arquivo.
Que tal atualizar a IU diretamente no thread filho após a conclusão da operação demorada?
De modo geral, ocorre o seguinte erro:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6891)
        at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:1048)
        at android.view.View.requestLayout(View.java:19781)
        at android.view.View.requestLayout(View.java:19781)
        at android.view.View.requestLayout(View.java:19781)
        at android.view.View.requestLayout(View.java:19781)
        at android.view.View.requestLayout(View.java:19781)
        at android.view.View.requestLayout(View.java:19781)
        at androidx.constraintlayout.widget.ConstraintLayout.requestLayout(ConstraintLayout.java:3172)
        at android.view.View.requestLayout(View.java:19781)
        at android.widget.TextView.checkForRelayout(TextView.java:7368)
        at android.widget.TextView.setText(TextView.java:4480)
        at android.widget.TextView.setText(TextView.java:4337)
        at android.widget.TextView.setText(TextView.java:4312)
        at icbc.agree.tmpapppp.MainActivity$2.run(MainActivity.java:37)

A partir da dica de código acima, não é difícil ver que a IU só pode ser atualizada no encadeamento principal, e é por isso que o encadeamento principal também é chamado de encadeamento de IU.
Mas quando iniciamos um thread filho em onCreate para alterar o texto de um TextView, ficaremos surpresos ao descobrir que o programa pode ser executado normalmente.
Na verdade, é porque o trabalho de verificação do thread é feito por ViewRootImpl, mas iniciar o thread filho em onCreate pode ter sido executado antes de o objeto ViewRootImpl ser criado. Método setText de TextView, se você não acreditar, você pode dormir um pouco antes de setText. que.

Como a IU não pode ser atualizada no thread filho, precisamos de um mecanismo para notificar o thread principal para atualizar a IU, que é de responsabilidade do Handler!

Como o Handler é implementado?
Para entender o princípio do Handler, precisamos conhecer vários conceitos:
Mensagem: A portadora de dados passados ​​e processados ​​pelo Handler
Message Queue: Message queue
Looper: Um iterador que constantemente obtém dados da fila de mensagens

Insira a descrição da imagem aqui
O fluxo de trabalho do Handler é mais ou menos como mostrado na figura acima. O Handler envia uma Mensagem por meio de métodos como sendMessage e a coloca em MessageQueue. O Looper constantemente tira os dados da fila e os distribui.

A seguir, observe o princípio de funcionamento do Handler a partir do código-fonte do sistema Android:
Primeiro, vamos examinar o trabalho no thread principal

public static void main(String[] args) {
        <-省略部分不相关代码->
        Looper.prepareMainLooper();

        <-省略部分不相关代码->

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

Vemos Looper.prepareMainLooper (); nesta linha de código, você pode ir para Looper para ver sua função, na verdade, é o método de preparação chamado, e sua função é criar uma fila de mensagens. O código é o seguinte:

		mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();

O último loop () no thread principal é chamado, agora vamos olhar o código correspondente:

public static void loop() {
         final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;
		<-省略代码部分->
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            <-省略代码部分->
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

           <-省略代码部分->
        }
    }

Vamos analisar o código acima, obtemos a Fila de Mensagens e, em seguida, iniciamos um loop infinito, retire a Mensagem por meio de queue.next (), se a mensagem estiver vazia, significa que não há mensagem na fila de mensagens, então pare a
última mensagem do loop . target.dispatchMessage (msg); Deve ser familiar, certo? Este destino é o Handler que envia a mensagem.
Agora, vamos dar uma olhada na parte Handler:
seja sendMessage ou sendEmptyMessage, sendMessageAtTime é finalmente chamado, portanto, veremos diretamente a parte do código-fonte de sendMessageAtTime.

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

Continue a olhar para baixo

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Você encontrou o alvo? Se tiver interesse, pode continuar procurando o código, na verdade é para adicionar Message à fila de mensagens
, até o momento todo o processo de Handler está resolvido!

Acho que você gosta

Origin blog.csdn.net/RobinTan24/article/details/108795499
Recomendado
Clasificación