Android的Handler

1. Handler是用于线程间通信,本质上是:

Handler调用发送方法,向与Looper绑定的消息队列写入消息,然后Looper.loop()会循环的从消息队列里拿出消息。并调用dispatchMessage处理消息。而需要此消息的线程会实现回调的handleMessage接口来处理消息。

2.举个例子:主线程调用子线程的Handler发送消息。

cpp 复制代码
package com.android.car.myapplication;


import static java.lang.Thread.currentThread;
import static java.lang.Thread.sleep;

import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private TextView textView;
    private Handler mainHandler;
    private Handler backgroundHandler;
    private Thread backgroundThread;

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

        textView = findViewById(R.id.textView);

        // 主线程的 Handler,用于更新 UI
        mainHandler = new Handler(Looper.getMainLooper());

        // 创建子线程并启动它
        backgroundThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("childThread prepare");
                Looper.prepare();

                // 在子线程中创建一个 Handler
                // 充当其他线程调用 backgroundHandler.sendMessage/backgroundHandler.post的接收端
                backgroundHandler = new Handler(Looper.myLooper()) {
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.what == 1) {
                            System.out.println("childThread receive Msg 1");
                        }
                    }
                };

                System.out.println("childThread Looping");
                Looper.loop();
            }
        });

        backgroundThread.start();

        // 等待线程跑起来
        try {
            sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        Message message = backgroundHandler.obtainMessage(1);
        backgroundHandler.sendMessage(message);
        System.out.println("MainThread: sendMsg 1");
    }
    
}
  • 日志
bash 复制代码
2024-11-07 16:25:54.908 12880-12902 System.out       com...myapplication  I  childThread prepare
2024-11-07 16:25:54.908 12880-12902 System.out       com...myapplication  I  childThread Looping
2024-11-07 16:25:56.910 12880-12880 System.out       com...myapplication  I  MainThread: sendMsg 1
2024-11-07 16:25:56.912 12880-12902 System.out       com...myapplication  I  childThread receive Msg 1

3. Handler使用的源码解析

  • 3.0 调用端使用sendMessage来发送消息
cpp 复制代码
Message message = backgroundHandler.obtainMessage(1);
backgroundHandler.sendMessage(message);

追sendMessage的调用栈,最终会调用到Handler.java的enqueueMessage函数

cpp 复制代码
    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;   // 
。。。。。
        return queue.enqueueMessage(msg, uptimeMillis);
    }

1)msg.target指定了this,即当前的Handler, 对应3.3中取出消息的地方msg.target为handler, 且这个Handler是backgroundHandler。

2)enqueueMessage将msg放入了消息队列。

  • 3.1 Looper.prepare();
cpp 复制代码
    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));
    }
  • 3.2 Handler backgroundHandler = new Handler(Looper.myLooper())
cpp 复制代码
    public Handler(@NonNull Looper looper) {
        this(looper, null, false);
    }



    @UnsupportedAppUsage
    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        this(looper, callback, async, /* shared= */ false);
    }


    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async,
            boolean shared) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
        mIsShared = shared;
    }
  • 3.3 Looper.loop(); 关键是loopOnce()从消息队列中循环拿消息,

假设现在调用了3.0中的sendMessage方法,此时消息队列中已经有了消息。

那么调用me.mQueue.next();即可拿到这条message.

再调用dispatchMessage处理消息。

cpp 复制代码
private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // might block
。。。。
        msg.target.dispatchMessage(msg);

。。。。。
        msg.recycleUnchecked();
。。。。。
        return true;
    }

3.0 讲过了msg.target是backgroundHandler,再往下查看dispatchMessage的实现:

cpp 复制代码
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);  // 调用者overide 实现的 handleMessage函数
        }
    }

会发现其中的handleMessage是在 2.当中实现的handleMessage方法。

4. nativePollOnce(ptr, nextPollTimeoutMillis);

nativePollOnce, 就是linux中的poll函数,可以让出线程对cpu的占用。

没有消息则线程进入空闲状态,不用一直调用来浪费cpu, 这种等待是非阻塞的。

5.总结:

在子线程中:

  • Looper.prepare()初始化了MessageQueue,与当前线程绑定。
  • Handler通过传入当前的Looper实现与Loop绑定,
  • Handler通过override实现了handleMessage。
  • 再通过Looper.loop()开启死循环来处理消息。

其他线程:

  • 调用子线程的Handler的post/sendMessage方法,来向目标线程的MessageQueue写入消息。

子线程:

  • 子线程调用Loop.loop()拿到消息,并调用dispatchMessage处理消息。
  • 在调用子线程override的handleMessage方法来处理来自其他线程的消息。
相关推荐
后端码匠2 小时前
MySQL 8.0安装(压缩包方式)
android·mysql·adb
梓仁沐白4 小时前
Android清单文件
android
董可伦6 小时前
Dinky 安装部署并配置提交 Flink Yarn 任务
android·adb·flink
每次的天空7 小时前
Android学习总结之Glide自定义三级缓存(面试篇)
android·学习·glide
恋猫de小郭7 小时前
如何查看项目是否支持最新 Android 16K Page Size 一文汇总
android·开发语言·javascript·kotlin
flying robot8 小时前
小结:Android系统架构
android·系统架构
xiaogai_gai8 小时前
有效的聚水潭数据集成到MySQL案例
android·数据库·mysql
鹅鹅鹅呢9 小时前
mysql 登录报错:ERROR 1045(28000):Access denied for user ‘root‘@‘localhost‘ (using password Yes)
android·数据库·mysql
在人间负债^9 小时前
假装自己是个小白 ---- 重新认识MySQL
android·数据库·mysql
Unity官方开发者社区9 小时前
Android App View——团结引擎车机版实现安卓应用原生嵌入 3D 开发场景
android·3d·团结引擎1.5·团结引擎车机版