Android Handler机制

Android Handler机制

Android Handler 机制是一种用于在 Android 应用中处理多线程和异步操作的机制。

设计特点

为了解决UI线程(主线程)和后台线程之间的通信和异步处理的问题。在Android应用中,特别是在UI开发中,有一些重要的原则需要遵循:

  1. 主线程负责UI更新: Android UI框架是单线程模型的,也就是说UI更新必须在主线程上进行。如果在主线程上执行耗时操作,会导致界面卡顿、失去响应等用户体验问题。

  2. 异步处理耗时操作: 为了避免在主线程上执行耗时操作,需要在后台线程上执行这些操作。但是,后台线程不能直接更新UI,因此需要一种机制来在后台线程执行任务后通知主线程更新UI。

为了解决这些问题,Android引入了Looper、Handler、MessageQueue和Message:

  • Looper: 负责创建一个消息队列,循环处理消息。

  • MessageQueue: 用于存储消息,按照一定规则将消息分发给Handler。

  • Handler: 用于发送和处理消息,通过与Looper关联,使得处理消息的操作在特定线程(通常是主线程)执行。

  • Message: 包含要处理的数据和操作的对象,通过Handler发送到MessageQueue中。

这个设计模型允许在主线程和后台线程之间进行有效的通信和任务分发,使得应用能够更好地响应用户输入、保持界面流畅。

简单的示例

下面是一个简单的示例,演示了如何使用 Handler 在后台线程中执行任务并在主线程中更新 UI:

public class ExampleActivity extends AppCompatActivity {
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_example);

        // 创建 Handler 对象,并关联主线程的 Looper
        handler = new Handler(Looper.getMainLooper());

        // 启动后台线程执行任务
        new Thread(new Runnable() {
            @Override
            public void run() {
                // 模拟耗时操作
                doBackgroundWork();

                // 在主线程中更新 UI
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        updateUI();
                    }
                });
            }
        }).start();
    }

    private void doBackgroundWork() {
        // 模拟耗时操作
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void updateUI() {
        // 在主线程中更新 UI
        TextView textView = findViewById(R.id.textView);
        textView.setText("Task completed!");
    }
}

在上述示例中,通过 Handler 将 updateUI() 方法安排到主线程执行,以确保在更新 UI 时不会出现线程问题。这样可以避免在后台线程中直接更新 UI,从而确保在主线程中执行 UI 操作。

 Handler 机制的核心组成部分

1. Handler:

Handler 是 Android 中的一个类,用于发送和处理消息以及运行代码块。主要功能包括:

  • 发送消息到消息队列。

  • 处理消息队列中的消息,执行相应的操作。

  • 安排代码块在指定的时间之后运行。

  • Handler 包含一个 Looper 对象

+-------------------------+
|       Handler           |
+-------------------------+
| - mLooper: Looper       |
| - mCallback: Callback   |
+-------------------------+
| + handleMessage(msg: Message): void |
| + dispatchMessage(msg: Message): void |
| + sendMessage(msg: Message): boolean |
| + post(runnable: Runnable): boolean |
+-------------------------+

2. Looper:

  • Looper 与每个线程关联,负责管理该线程的消息队列。

  • 包含一个 MessageQueue 对象,它负责管理线程的消息队列。

  • 通过 Looper.getMainLooper() 可以获取主线程的 Looper,在其他线程中可能需要显式创建。

  • 在主线程中,Looper 默认是启动的,因此可以直接使用 Handler

+-------------------------+
|        Looper           |
+-------------------------+
| - mQueue: MessageQueue  |
| - mThread: Thread       |
+-------------------------+
| + prepare(): void       |
| + loop(): void          |
| + myLooper(): Looper    |
| + getMainLooper(): Looper|
+-------------------------+

默认情况下当我们创建一个新的线程的时候,这个线程里面是没有消息队列MessageQueue的。为了能够让线程能够绑定一个消息队列,我们需要借助于Looper:首先我们要调用Looper的prepare方法,然后调用Looper的loop方法。

prepare()方法
public static final void prepare() {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(true));

sThreadLocal是一个ThreadLocal对象,可以在一个线程中存储变量。可以看到,将一个Looper的实例放入了ThreadLocal,并且先判断了sThreadLocal.get是否为null,否则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例。

loop()方法
    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();
            if (msg == null) {
                // 如果Message链表的头结点为空,说明没有消息可执行,此次loop停止
                return;
            }
            ......
            try {
                // 执行消息承载的方法
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ......
            msg.recycleUnchecked();
        }
    }

msg.target.dispatchMessage(msg);

msg的target属性是Handler,每个msg包含一个handler对象,该代码的意思是让Message所关联的Handler通过dispatchMessage方法让Handler处理该Message。

Looper主要作用

1.与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。

2.loop()方法,不断从MessageQueue中去取消息,交给消息Message的target属性,即Handler的dispatchMessage去处理。

3. MessageQueue:

MessageQueue 包含 Message 对象。

MessageQueue 是消息队列的实现。它按照消息的时间戳(先进先出)的顺序存储消息。

+-------------------------+
|     MessageQueue        |
+-------------------------+
| - mMessages: Message    |
+-------------------------+
| + enqueueMessage(msg: Message, when: long): void |
| + next(): Message       |
| + removeMessages(what: int, obj: Any): void       |
+-------------------------+

 4. Message:

Message 是一个包含要处理的数据和操作的对象。Handler 通过 sendMessage() 方法发送消息到消息队列,然后由 Looper 传递给相应的 Handler 进行处理。

+-------------------------+
|        Message          |
+-------------------------+
| - what: int             |
| - obj: Object           |
| - target: Handler       |
| - when: long            |
+-------------------------+
| + obtain(): Message     |
| + recycle(): void       |
+-------------------------+

5. Runnable 和 Message 处理:

Handler 可以处理两种类型的任务:通过 post() 方法安排的 Runnable 对象,以及通过 sendMessage() 发送的 Message 对象。

总结

  • Handler 包含一个 Looper 对象,Looper 包含一个 MessageQueue 对象,MessageQueue 包含 Message 对象。

  • Handler 可以发送 Message 到 MessageQueue 中,Looper 会循环处理 MessageQueue 中的消息,调用相关联的 Handler 的 handleMessage 方法。

  • Looper主要作用

    1.与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。

    2.loop()方法,不断从MessageQueue中去取消息,交给消息Message的target属性,即Handler的dispatchMessage去处理。

  • Message 中包含了要处理的数据和操作,可以通过 Handler 的 sendMessage() 方法发送到消息队列。

关于更详细的源码讲解可参考(特别感谢):

Android 中为什么需要 Handler? - 知乎

Android Handler机制-Looper、Handler、MessageQueue、Message的关系 - 掘金

Android应用程序消息处理机制(Looper、Handler)分析_罗升阳 handler-CSDN博客

  • 23
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值