站在线程角度看Android Handler 机制

本文深入探讨Android中的Handler机制,解析其工作原理,包括Handler、Looper和Message三个核心组件的作用及交互过程,帮助开发者理解跨线程通信机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        Android开发的小伙伴们对Handler机制应该非常熟悉了,Handler机制主要用于完成两个线程间互相通信,最典型的应用就是在子线程中执行一系列任务,然后通知UI线程更新UI。主要与Handler 、 Looper 、Message 这三个类相关,其中Handler负责处理(发送,接收并处理)消息,Looper负责维护一个消息队列,并通过一个不断的循环从队列里取出消息交给Handler处理,Message就是消息本身。看过不少文章,作为菜鸟的我心中还是有一些疑问,终于抽时间好好梳理了一下,不多说,直接上问题。

        为了方便描述,我们把UI线程成为主线程,把其他我们自己创建的线程,称为子线程。

        问题一:Handler机制中如何将两个线程联系起来,站在线程的角度,各个线程都做了些啥,执行了哪些语句?

        问题二:UI线程需要处理很多事务且不能堵塞,一个线程只有一个控制权,一个线程中sendMessage之后,处理线程的控制权会在什么时机处理发送过来的message

        问题三:与UI线程绑定的Handler的postDelay(Runnable r, Long delay)方法什么情况下会阻塞UI线程?为啥?   

        从问题一开始,先看一个典型的场景:子线程执行了handler.sendMessage(1),结果是主线程执行了handleMessage方法里的textView.setText();

public class MainActivity extends Activity {  
    private TextView textView;  
    private Handler handler = new Handler(){  
        public void handleMessage(android.os.Message msg) {  
            textView.setText("Handler");  
        };  
    };  
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        textView =(TextView) findViewById(R.id.textView);  
        new Thread(){  
            public void run() {//这里是子线程在执行  
                try {  
                    Thread.sleep(1000);  
                    handler.sendEmptyMessage(1); // 
                } catch (InterruptedException e) {  
                    // TODO Auto-generated catch block  
                    e.printStackTrace();  
                }    
            };  
        }.start();  
    }   
}  


         很多文章介绍过,handler的send相关方法和post相关方法最终都会到sendMessageAtTime( Message msg, long uptimeMillis )。我们这里重点关注线程的执行步骤,或者说线程控制权的一步一步转移,看我们handler.sendMessage(1)之后的调用链:

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

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;//记住这个target,this就是Handler的一个对象了。。。
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

         子线程执行了handler.sendMessage(1),所以以上调用都是子线程来执行的。在最后一个方法enqueueMessage()中,msg的target指向当前的Handler对象handler。

         所以,例子中的子线程的控制权在以下方法中转移:sendMessage(Message msg)

                                                                                      --->sendMessageDelayed(Message msg, long delayMillis)

                                                                                      --->sendMessageAtTime(Message msg, long uptimeMillis)

                                                                                      --->enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)

                                                                                      --->enqueueMessage() // 这个enqueueMessage是MessageQueue的方法。

         所以子线程就是往handler相关联的MessageQueue里丢了一条Message,然后控制权会回到该线程的run()方法里,去执行handler.sendMessage(1)的下一条语句,在我们的例子里,run()方法执行完毕。子线程进入死亡状态,线程对象等待被回收。

        在Handler机制中,Looper负责从MessageQueue中取出message并做相应处理。子线程既然往handler对应的MessageQueue里丢了一条Message,那肯定这MessageQueue对应的线程和对应的Looper在处理这条message。在sendMessageAtTime(Message msg, long uptimeMillis)方法中,看到所操作的消息队列mQueue,看一下这个mQueue是什么时候被赋与对象(值)或者说初始化的。

找到一个Handler的构造:

public Handler(Callback callback, boolean async) {
        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;
    }
例子中在为handler实例化的时候使用的无参构造直接调用了上面的构造:
public Handler() {
        this(null, false);
    }

       因为我们是在主线程中创建的hadler, 构造方法中调用Looper.myLooper()来获取Looper对象也就自然归属于主线程,也就是说这里的Looper和MessageQueue都是与UI线程相对应的,那例子中丢到这个messageQueue里的Message也自然由UI线程去取,并在loop()方法中执行msg.target.dispatchMessage(msg),这里msg.target就是刚刚被赋予的handler,最终由UI线程执行dispatchMessage()来处理这条消息。Looper.myLooper()来获取Looper对象期间的线程对应关系后边再分析。

public static void loop() {//创建Handler的线程必然执行了这个loop(),主线程默认执行了Looper.prepare()和Looper.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 (;;) {//这个是主线程对应的Looper的loop方法的话,主线程的控制权会在这个for循环里直到App退出。
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            msg.target.dispatchMessage(msg);//target 就是发送该msg的Handler对象
            msg.recycleUnchecked();
        }
    }
      来看 dispatchMessage:
public void dispatchMessage(Message msg) {
        if (msg.callback != null) {//handler执行post相关的方法时,丢进来的Runnble对象会作为msg.callback,优先执行该回调
            handleCallback(msg);//
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {//看到回调的的handleMessage,我们并没有在定义Handler的时候生成该回调。
                    return;
                }
            }
            handleMessage(msg);//终于执行了我们在Handler定义时覆写的handleMessage了!!!!
        }
    }
所以, UI线程执行了Looper 的 prepare() loop()并在loop的无限循环中执行了Handler的 dispatchMessage(Message msg)和 handleMessage(Message msg)。


总结:Handler对应一个线程,一个线程对应且只对应一个Looper对象,一个线程对应且只对应一个MessageQueue。(注意,没有说一个线程对应一个handler)

          在发送消息阶段,Handler sendMessage可以在任意线程发出,但是发出去的message会被入队到该发出消息的Handler对应的线程关联的消息队列。

          在消息的处理阶段,也是由发出消息的Handler对应的线程通过Looper.loop()方法来处理消息。

          到这里就可以从逻辑上解释,为什么我们自己新建的线程必须执行Looper.prepare()和Looper.loop()方法才能创建handler,因为不执行Looper.prepare(),handler发送的message不知道入队到哪个消息队列,发送消息无从谈起,不执行Looper.loop(),线程就没有不断地从消息队列取消息并处理的过程,处理消息也就无从谈起。

          当然,其实Handler提供了可以传入Looper参数的构造,子线程可以在不执行Looper.prepare()和Looper.loop()方法的情况下通过传入一个Looper对象创建handler,这时Looper对应的线程即是该handler对应的线程。所谓Looper对应的线程由其prepare()方法的调用线程决定。看Looper的prepare()方法:

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

prepare()方法通过ThreadLocal给执行prepare的线程绑定了一个 Looper对象。

          ThreadLocal提供一种线程的变量管理机制,主要是set(Tvalue)和get(value)方法,通过调用sThreadLocal.set(value)可以为调用线程设置一个变量,这里就是给执行prepare的线程设置了一个looper对象,也是通过这个机制,保证了线程与Looper的一对一关系。上文提到的Looper.myLooper()获取的looper对象就是通过这个机制获取的。

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
     通过sThreadLocal.get()就可以获取当前线程调用set方法时设置的值(对象),ThreadLocal保证了不同的线程调用get时,得到的变量都是该线程set的值(对象)。

         到这里就可以解答问题二了,在一个线程发送message后,对应的处理线程必须执行Looper.loop(),它的控制权就一定在loop()方法中不断循环,才能及时从消息队列中取出消息做相应的处理。

          问题三,先说答案:UI线程绑定的Handler的postDelay(Runnable r, Long delay)方法delay多长时间都不会阻塞UI线程,但是r的run()方法如果执行时间过长,会阻塞。
                    

 参考文章:

          《线程的私家小院:ThreadLocal》点击打开链接

           鸿洋大神的:点击打开链接

         《Android中的Handler解析》点击打开链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值