站在线程角度看Android Handler 机制

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

        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解析》点击打开链接

<think>我们正在讨论Android Handler机制中的同步消息和异步消息的区分方法。根据之前的引用和问题,我们需要从Handler、Message、MessageQueue和Looper的角度来解释。 引用[2]提到:同步屏障是一种同步机制,用于确保某些操作按照预定的顺序执行,在Handler中是一种优先级策略,它会让异步消息的优先级高于同步消息。这是因为有些消息(如UI刷新)需要最高优先级,确保立即执行。 引用[3]说明了Handler机制的基本组成:Handler、Message、MessageQueue和Looper。其中,MessageQueue是一个按触发时间排序的单向链表。 在Android的消息机制中,消息分为三种类型:同步消息、异步消息和同步屏障(一种特殊的消息)。我们关注的是同步消息和异步消息的区分方法。 区分方法: 1. 消息标记:在Message对象中有一个标志位`isAsynchronous`,用于标识该消息是否为异步消息。 - 同步消息:`isAsynchronous()`返回false。 - 异步消息:`isAsynchronous()`返回true。 2. 消息发送方式: - 同步消息:通常通过`Handler.sendMessage()`或`Handler.post()`发送的消息默认是同步消息。 - 异步消息:通过`Handler.postAtTime()`、`Handler.postDelayed()`等发送的消息默认也是同步消息。要发送异步消息,需要使用特定的方法: a. 在创建Handler时,指定Handler为异步的:通过`Handler(Handler.Callback callback, boolean async)`构造函数的第二个参数为true。 b. 使用Message的setAsynchronous(true)方法:在构造Message后,调用`message.setAsynchronous(true)`,然后发送。 3. 消息处理优先级(同步屏障): 当MessageQueue中存在同步屏障(通过`postSyncBarrier()`方法设置)时,它会阻止同步消息的处理,只允许异步消息被处理。同步屏障相当于一个特殊的消息,它的target(Handler)为null。在同步屏障被移除之前,同步消息都会被阻塞,而异步消息则不受影响。 4. 如何设置同步屏障? 同步屏障通常用于系统级的高优先级操作(如UI绘制)。我们可以通过`MessageQueue.postSyncBarrier()`方法设置,但此方法是隐藏的(@hide),一般应用开发者不会直接使用。移除同步屏障使用`MessageQueue.removeSyncBarrier(int token)`。 示例代码: 发送异步消息: ```java // 方法1:创建异步Handler Handler asyncHandler = new Handler(Looper.getMainLooper(), handlerCallback, true); // 第三个参数为true表示异步 // 方法2:使用Message的setAsynchronous方法 Message msg = Message.obtain(); msg.setAsynchronous(true); handler.sendMessage(msg); ``` 设置同步屏障(需要反射,不推荐在应用层使用): ```java // 获取MessageQueue MessageQueue queue = Looper.getMainLooper().getQueue(); Method method = MessageQueue.class.getDeclaredMethod("postSyncBarrier"); method.setAccessible(true); int token = (int) method.invoke(queue); // 设置同步屏障 // 移除同步屏障 Method removeMethod = MessageQueue.class.getDeclaredMethod("removeSyncBarrier", int.class); removeMethod.setAccessible(true); removeMethod.invoke(queue, token); ``` 注意:在应用开发中,我们通常不需要手动设置同步屏障,系统在需要的时候(例如ViewRootImpl中处理VSYNC信号时)会自动设置。而异步消息的使用也需谨慎,避免滥用导致消息处理顺序混乱。 总结区分方法: - 通过消息的`isAsynchronous()`标志位判断。 - 异步消息的发送需要显式设置(通过异步Handler或调用Message的setAsynchronous(true))。 - 在同步屏障存在时,只有异步消息会被处理,同步消息被阻塞。 引用[2]中提到同步屏障的存在是为了让异步消息(如UI刷新)优先执行,所以在需要及时响应的场景(如动画、UI更新)会使用异步消息。 相关问题: 1. 同步屏障在Android系统中的应用场景有哪些? 2. 如何避免异步消息滥用导致的Handler消息队列混乱? 3. 在自定义Looper线中如何处理同步消息和异步消息? 4. Handler消息机制如何保证线安全? 注意:在回答中,我们需要在引用内容的末尾添加引用标识,例如[^2]。根据用户提供的引用,我们主要参考了引用[2]和引用[3]。 </think>### Android Handler 机制中同步消息与异步消息的区分方法AndroidHandler 消息机制中,同步消息和异步消息的核心区分方式如下: --- #### **1. 消息类型标识(核心区分点)** - **异步消息**:通过 `Message.setAsynchronous(true)` 显式标记 ```java Message asyncMsg = handler.obtainMessage(); asyncMsg.setAsynchronous(true); // 关键标识 handler.sendMessage(asyncMsg); ``` - **同步消息**:未显式设置异步标志的消息均为同步消息(默认类型) ```java // 默认创建的消息都是同步消息 Message syncMsg = handler.obtainMessage(); handler.sendMessage(syncMsg); ``` --- #### **2. 消息处理优先级(同步屏障机制)** 当存在 **同步屏障(Sync Barrier)** 时,两种消息的处理优先级不同: - **同步屏障**:通过 `MessageQueue.postSyncBarrier()` 设置的特殊标记节点 (系统在 VSYNC 信号触发 UI 渲染时自动设置)[^2] - **处理规则**: - 屏障存在时:优先处理**异步消息**(如 UI 刷新消息) - 屏障移除后:按时间顺序处理**同步消息** ```mermaid graph LR A[同步屏障] --> B{消息队列} B --> C[异步消息] --> 优先执行 B --> D[同步消息] --> 暂停处理 ``` --- #### **3. 消息发送方式区分** | **特性** | 同步消息 | 异步消息 | |-------------------|-----------------------------------|-----------------------------------| | **Handler 创建** | `new Handler()` | `new Handler(true)` // 异步构造器 | | **标记方式** | 默认无标记 | `msg.setAsynchronous(true)` | | **系统使用场景** | 普通任务 | VSYNC 触发的 UI 刷新 [^2] | | **执行优先级** | 常规顺序 | 可突破同步屏障优先执行 | --- #### **4. 实战验证方法** 可通过拦截 `MessageQueue.next()` 验证消息类型: ```java // 反射获取 MessageQueue MessageQueue queue = Looper.getMainLooper().getQueue(); Field field = MessageQueue.class.getDeclaredField("mMessages"); field.setAccessible(true); // 检查消息类型 Message msg = (Message) field.get(queue); if (msg != null) { if (msg.isAsynchronous()) { Log.d("MSG_TYPE", "异步消息"); } else { Log.d("MSG_TYPE", "同步消息"); } } ``` --- #### **关键注意事项** 1. **异步消息慎用**:仅系统级高优先级任务(如 UI 刷新)需要异步消息,滥用会导致消息时序混乱 2. **屏障自动管理**:同步屏障由系统自动设置/移除(如 `ViewRootImpl` 处理 VSYNC 时) 3. **自定义异步 Handler**: ```java // 创建异步 Handler Handler asyncHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() { @Override public boolean handleMessage(Message msg) { // 处理异步消息 return true; } }, true // 关键异步标识 ); ``` --- ### 相关问题 1. 同步屏障的工作原理是什么?如何手动创建和移除同步屏障? 2. 在自定义 Looper 线中如何处理异步消息? 3. 异步消息是否会影响 Android 应用的 UI 渲染性能? 4. Handler 消息机制中如何避免异步消息导致的时序错乱问题? [^1]: 通过 Handler 消息机制实现线间通信保证 UI 流畅性 [^2]: 同步屏障确保异步消息优先级高于同步消息 [^3]: Android 消息机制Handler、MessageQueue、Looper 共同支撑
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值