Handler原理讲解

这篇博客详细解析了Android中Handler的工作原理,包括Handler、Looper、MessageQueue之间的关系,以及如何在线程间切换处理消息。文中强调主线程的Looper在ActivityThread的main()方法中创建,并通过prepareMainLooper启动消息轮询。Handler的创建方式不同会影响消息的处理路径,如post()实际上是通过Runnable回调处理,而sendMessage()则调用handleMessage()。此外,还探讨了如何在子线程中创建自己的Looper来处理消息。
摘要由CSDN通过智能技术生成

Handler原理讲解

主要是讲解(记录)此前看到的Handle如何做线程切换原理

Handler涉及类

Handler、线程、Looper、MessageQueue关系

  1. 一个线程只有一个Looper和一个MessageQueue

  2. 子线程创建Handler对象时需要传入Looper,如果是将消息发送到主线程的话

  3. 每个Handler都会关联一个消息队列,消息队列被封装在Looper中,而每个Looper又会关联一个线程(Looper通过ThreadLocal封装)。最终就等于每个消息队列都会关联一个线程

Looper是轮询器,不断轮询MessageQueue,取出Message,如果有Message,就取出,并交予Handler处理。需要注意的是线程与Looper和MessageQueue是一一对应的

解析

Handler创建

平时使用Handler大概是这样:

//在主线程中创建Handler并使用
Handler handler = new Handler();
handler.post(new Runnable() {
    @Override
    public void run() {
        loadData();
    }
});

//在子线程创建Handler并使用
Handler handler = new Handler();
handler.post(new Runnable(getMainLooper()) {
    @Override
    public void run() {
        loadData();
    }
});

如上所示,(都是将消息发送到主线程中处理)如果在子线程中创建Handler,比在子线程中创建Handler不一样的是仅仅在创建Handler对象时使用的构造器不一样:在子线程中传入了Looper对象(来自getMainLooper())。查看Handler构造器:

public Handler() {
    this(null, false);
}

public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        FIND_POTENTIAL_LEAKS一直都会是false,不需要关心该控制语句
       ...省略
    }

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

public Handler(Looper looper) {
    this(looper, null, false);
}

public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

这也就是说,在以上两种创建Handler的方式中,如果有传入Looper对象,那么Handler就直接使用传入的Looper对象,否则就使用Looper.myLooper();获取到的Looper对象,如下:

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

通过myLooper()获取到的Looper对象是sThreadLocal.get()返回值,那么sThreadLocal是什么时候调用sThreadLocal.set的呢?这样get到的值才不会是null,而在set之前,肯定是先创建Looper对象,才能set。Handler对象是在程序中创建,那么Looper是在什么时候去创建的呢?答案是在ActivityThread的main()中

ActivityThread的main()方法是程序入口,与Java程序一致,在main()方法中,

public static void main(String[] args) {
    ...省略代码...
     Looper.prepareMainLooper();    
    ...省略代码...    
    Looper.loop();
}

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

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

/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

在prepareMainLooper方法中调用了prepare()方法,创建Looper对象和MessageQUeue对象,并通过sThreadLocal保存,最后通过myLooper()方法取出sThreadLocal保存的Looper引用。即在main()方法中就已经创建了Looper,这个Looper对象就是主线程的Looper对象

sendMessage/post

Handler通过sendMessage和post等方法发送消息,如下:

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

public final boolean post(Runnable r)
{
    return  sendMessageDelayed(getPostMessage(r), 0);
}

/**
 * Enqueue a message into the message queue after all pending messages
 * before (current time + delayMillis). You will receive it in
 * {@link #handleMessage}, in the thread attached to this handler.
 *  
 * @return Returns true if the message was successfully placed in to the 
 *         message queue.  Returns false on failure, usually because the
 *         looper processing the message queue is exiting.  Note that a
 *         result of true does not mean the message will be processed -- if
 *         the looper is quit before the delivery time of the message
 *         occurs then the message will be dropped.
 */
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

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

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

post(Runnable)最后还是调用了sendMessageDelayed(),两者殊途同归。需要注意的是最后一个方法enqueueMessage(MessageQueue , Message , long ),在这里设置了msg.target = this,也就是target的值就是当前Handler对象本身

ActivityThread的main()方法中,在创建Looper对象后,随后就启动了轮询,即调用了loop()

public static void main(String[] args) {
    ...省略
    Looper.prepareMainLooper();
    ...省略
    Looper.loop();
    ...省略
}

//Looper
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 (;;) {
        ...省略
        try {
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...省略
    }
    ...省略
}

此前已经提到Handler对象在发送消息时就已经设置了target值。也就是说轮询时取出放进去的Message对象,并调用Handler的dispatchMessage()去分发,去消费消息,那么继续查看Handler类的dispatchMessage方法

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

在这里判断发送消息方式

  • post(Runnable)系列

    在上文其实能看到如果通过post等方法发送消息,其实就是调用sendMessageDelayed()方法,并将Runnable类型的对象包装成Message类型的对象,并赋值给Message的callback,最后才将Message对象插入到MessageQueue

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

    在如上那段代码中,对Message对象的callback变量进行赋值,所以在进行消息分发时,会调用handleCallback()处理,如下所以,其实就是直接执行了post()传入Runnable对象的run()方法,所以使用post发送消息可直接在run()方法中处理后续

    private static void handleCallback(Message message) {
        message.callback.run();
    }
    
  • sendMessage(Message)系列

    在dispatchMessage()中,如果是通过sendMessage系列方法来完成,根据如上所示的创建Handler方式,如果不是显式地调用了带有Callback类型的参数的构造器的话,那么mCallback的值显然是为null,即会直接Handler对象的handleMessage()方法,如果在创建Handler对象时,有去重写handleMessage()方法,就会执行重写后的handleMessage()方法。另外,Handler默认的handleMessage()是空的,里面没有任何逻辑

    //Handler.java
    /**
     * Subclasses must implement this to receive messages.
     */
    public void handleMessage(Message msg) {
    }
    

子线程中自己处理消息

此前所说的都是在子线程中发送消息到主线程,由主线程去处理结果,那么子线程是否也能自己发送消息给自己使用,达到解决自取所需的效果?

其实是可以的,此前都是将消息发送到主线程的Looper,自然是在主线程处理消息。所以只要为子线程创建一个Looper,并启动Looper轮询即可,如下:

new Thread(new Runnable() {
    Handler handler = null;
    @Override
    public void run() {
        //为当前线程创建Looper
        Looper.prepare();
        handler = new Handler();
        //启动消息轮询,为当前线程
        Looper.loop();
    }
});

而主线程的Looper都是在ActivityThread的main()中完成对主线程的Looper的创建和启动

解决的问题

看完本文应能了解以下问题

  1. 多个Handler发送消息到消息队列,怎么找到对应的Handler处理结果?

    答案是Message的target变量

  2. 子线程为什么不能使用无参构造器创建Handler对象

    主线程能使用Handler的无参构造器创建Handler对象是因为直接使用了在ActivityThread创建的属于主线程的Looper对象。子线程并未手动创建Looper对象,所以不能这么用

  3. 子线程怎么做到切换到主线程,做到将消息发送到主线程,让主线程处理后续

    子线程创建Handler使用主线程的Looper对象就可以了

    new Thread(new Runnable() {
        Handler handler = null;
        @Override
        public void run() {
            handler = new Handler(getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    loadData();
                }
            });
        }
    });
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值