当前位置: 首页 > news >正文

安卓Handler和Looper的学习记录

Handler和Looper的关系

  1. 基本概念

Looper(循环器):
●负责管理消息队列(MessageQueue)
●不断从消息队列中取出消息并分发给对应的Handler处理
●每个线程只能有一个Looper

Handler(处理器):
●负责发送和处理消息
●与特定的Looper关联
●可以发送消息到消息队列,也可以处理从消息队列中取出的消息

  1. 关系说明
Handler ←→ Looper ←→ MessageQueue

●Handler依赖Looper:Handler需要绑定一个Looper才能工作
●Looper管理消息队列:Looper负责从MessageQueue中取出消息
●Handler处理消息:Looper将消息分发给对应的Handler处理

  1. 工作流程

●创建Looper:线程调用Looper.prepare()创建Looper
●启动循环:调用Looper.loop()开始消息循环
●Handler发送消息:Handler调用sendMessage()或post()发送消息到队列
●Looper分发消息:Looper从队列取出消息,调用Handler的handleMessage()方法

  1. 在子线程中创建Handler
// 在子线程中创建Handler
class WorkerThread extends Thread {private Handler handler;@Overridepublic void run() {// 1. 准备LooperLooper.prepare();// 2. 创建Handler(自动绑定当前线程的Looper)handler = new Handler() {@Overridepublic void handleMessage(Message msg) {// 处理消息switch (msg.what) {case 1:// 处理消息类型1break;}}};// 3. 启动消息循环Looper.loop();}public void sendMessage() {// 发送消息handler.sendEmptyMessage(1);}
}

其中Looper.prepare() 和Looper.loop()是必须的,Looper.prepare() 会自动为当前线程创建一个Looper对象,而Looper.loop()会将创建好的Looper运行起来,如果没有,则会抛出异常。

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

上述是Looper.prepare()创建Looper的过程,可以看出,会先进行检验当前是否存在Looper,如果存在则抛出RuntimeException异常,如果不存在,则创建一个新的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.");}if (me.mInLoop) {Slog.w(TAG, "Loop again would have the queued messages be executed"+ " before this one completed.");}me.mInLoop = true;...}public static @Nullable Looper myLooper() {return sThreadLocal.get();}

上述是Looper.loop()的源码,可以看出,一开始会检测当前线程是否存在一个Looper,如果不存在,则抛出RuntimeException异常。

  1. 主线程的特殊性

主线程(UI线程)默认就有Looper,所以可以直接创建Handler:

// 主线程中直接创建Handler
Handler mainHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {// 处理消息}
};

Handler线程间通信

  1. 子线程 → 主线程
public class MainActivity extends AppCompatActivity {private Handler mainHandler; // 绑定主线程的Looper@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 在主线程创建Handler,自动绑定主线程的LoopermainHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {// 这个代码在主线程执行updateUI();}};// 启动子线程new Thread(() -> {// 子线程执行耗时操作String result = doHeavyWork();// 发送消息到主线程mainHandler.post(() -> {// 这个代码在主线程执行updateUI(result);});}).start();}
}
  1. 主线程 → 子线程
public class WorkerThread extends Thread {private Handler workerHandler; // 绑定子线程的Looper@Overridepublic void run() {// 1. 准备子线程的LooperLooper.prepare();// 2. 创建Handler,绑定到当前子线程的LooperworkerHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {// 这个代码在子线程执行processTask(msg.obj.toString());}};// 3. 启动消息循环Looper.loop();}// 提供方法让主线程发送消息给子线程public void sendTask(String task) {if (workerHandler != null) {Message msg = Message.obtain();msg.obj = task;workerHandler.sendMessage(msg);}}
}// 在主线程中使用
public class MainActivity extends AppCompatActivity {private WorkerThread workerThread;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 启动工作线程workerThread = new WorkerThread();workerThread.start();// 主线程发送任务给子线程findViewById(R.id.button).setOnClickListener(v -> {workerThread.sendTask("处理这个任务");});}
}
  1. 子线程 → 子线程
public class ThreadA extends Thread {private Handler threadBHandler; // 绑定到线程B的Handlerpublic ThreadA(Handler threadBHandler) {this.threadBHandler = threadBHandler;}@Overridepublic void run() {// 线程A的工作while (true) {// 做一些工作String result = doWork();// 发送结果给线程BthreadBHandler.post(() -> {// 这个代码在线程B中执行processResult(result);});Thread.sleep(1000);}}
}public class ThreadB extends Thread {private Handler threadBHandler;@Overridepublic void run() {Looper.prepare();// 创建Handler,绑定到线程BthreadBHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {// 在线程B中处理消息System.out.println("线程B收到消息: " + msg.obj);}};Looper.loop();}public Handler getHandler() {return threadBHandler;}
}// 使用示例
public class ThreadCommunicationExample {public void startThreads() {// 启动线程BThreadB threadB = new ThreadB();threadB.start();// 等待线程B的Handler准备就绪while (threadB.getHandler() == null) {Thread.sleep(10);}// 启动线程A,并传入线程B的HandlerThreadA threadA = new ThreadA(threadB.getHandler());threadA.start();}
}
  1. 多线程Handler通信架构
public class MultiThreadHandler {private Handler mainHandler;    // 主线程Handlerprivate Handler networkHandler; // 网络线程Handlerprivate Handler dbHandler;      // 数据库线程Handlerpublic MultiThreadHandler() {// 主线程HandlermainHandler = new Handler(Looper.getMainLooper());// 启动网络线程startNetworkThread();// 启动数据库线程startDatabaseThread();}private void startNetworkThread() {new Thread(() -> {Looper.prepare();networkHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {// 在网络线程处理网络请求String response = performNetworkRequest((String) msg.obj);// 发送结果给主线程mainHandler.post(() -> {updateUI(response);});}};Looper.loop();}).start();}private void startDatabaseThread() {new Thread(() -> {Looper.prepare();dbHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {// 在数据库线程处理数据操作saveToDatabase((String) msg.obj);// 发送结果给主线程mainHandler.post(() -> {showSaveSuccess();});}};Looper.loop();}).start();}// 主线程发送网络请求public void requestData(String url) {if (networkHandler != null) {Message msg = Message.obtain();msg.obj = url;networkHandler.sendMessage(msg);}}// 主线程保存数据public void saveData(String data) {if (dbHandler != null) {Message msg = Message.obtain();msg.obj = data;dbHandler.sendMessage(msg);}}
}

消息队列为空的情况下,Looper处于什么状态

  1. Looper的核心状态:等待状态

当消息队列为空时,Looper处于等待状态(WAITING),具体表现为:

// Looper.loop() 的核心逻辑
public static void loop() {final Looper me = myLooper();final MessageQueue queue = me.mQueue;for (;;) {// 关键:当队列为空时,next()会阻塞Message msg = queue.next(); // 这里会阻塞等待if (msg == null) {return; // 只有Looper被退出时才返回null}// 处理消息msg.target.dispatchMessage(msg);msg.recycleUnchecked();}
}
  1. MessageQueue.next() 的阻塞机制
// MessageQueue.next() 的简化逻辑
Message next() {for (;;) {// 检查是否有消息if (mMessages != null) {// 有消息,返回消息return mMessages;}// 没有消息,进入等待状态nativePollOnce(ptr, nextPollTimeoutMillis);// 这里会调用native方法,让线程进入等待状态}
}

当Handler发送消息 → MessageQueue.enqueueMessage() → native层的wake()方法,wake()会向等待的线程发送信号(如写管道、futex唤醒等),等待的线程被唤醒,nativePollOnce返回,Java层继续处理消息。

Handler/Looper/MessageQueue的线程安全机制

  1. 设计原则

●每个Looper/MessageQueue只属于一个线程
Handler只能处理它所绑定线程的消息队列,消息的处理始终在同一个线程内完成。
●消息的入队是线程安全的
任何线程都可以安全地向Handler发送消息(即向MessageQueue入队)。
●消息的出队和处理只在Looper线程内
只有Looper线程会不断从MessageQueue取出消息并处理。

  1. 消息入队的线程安全
// MessageQueue.enqueueMessage()
synchronized (this) {// ... 省略部分代码 ...// 将消息插入队列msg.next = p;prev.next = msg;// ... 省略部分代码 ...
}

synchronized保证了多线程同时入队时的互斥,防止数据竞争。

  1. 消息出队和处理

只有Looper线程会调用MessageQueue.next(),因此出队和处理消息不需要额外加锁,天然线程安全。

  1. 常见线程安全误区

●误区1:Handler可以跨线程处理消息
实际上,Handler只能在它绑定的Looper线程处理消息。
●误区2:Handler的成员变量天然线程安全
只有消息队列是线程安全的,Handler自己的成员变量如果被多个线程访问,需要自己加锁或用线程安全的数据结构。

  1. 进阶:HandlerThread的线程安全

HandlerThread内部自动为你创建了一个带Looper的线程,Handler绑定到这个线程,消息的处理天然线程安全。

HandlerThread thread = new HandlerThread("MyThread");
thread.start();
Handler handler = new Handler(thread.getLooper());
http://www.dtcms.com/a/319496.html

相关文章:

  • 计算机视觉-OpenCV
  • GPT-5 将在周五凌晨1点正式发布,王炸模型将免费使用??
  • Android 之 Kotlin 扩展库KTX
  • 突破距离桎梏:5G 高清视频终端如何延伸无人机图传边界
  • RK3568项目(十三)--linux驱动开发之基础通讯接口(下)
  • 闪迪 SN8100 旗舰固态评测:读 14.9GB/s,写 14.0GB/s 的性能怪兽
  • 8.结构健康监测选自动化:实时数据 + 智能分析,远超人工
  • 深度学习中主要库的使用:(一)pandas,读取 excel 文件,支持主流的 .xlsx/.xls 格式
  • Flink-1.19.0-核心源码详解
  • 网站IP被劫持?三步自建防护盾
  • 【中微半导体】BAT32G139 逆变器,中微半导体pack包安装使用说明(参考例程获取DemoCode)
  • 51c大模型~合集165
  • 【动态规划 | 完全背包】动态规划经典应用:完全背包问题详解
  • 【CS创世SD NAND征文】额贴式睡眠监测仪的数据守护者:存储芯片如何实现7×24小时安眠状态下的全时稳定记录
  • Redis面试精讲 Day 13:Redis Cluster集群设计与原理
  • Flutter 三棵树
  • 数字取证:可以恢复手机上被覆盖的数据吗?
  • 【免费】小学数学算术专项突破随机生成加法减法乘法除法
  • 无人机计算机视觉数据集-7,000 张图片 空域安全监管 无人机反制系统 智能安防监控 交通执法应用 边境管控系统 赛事安保服务
  • 香港网站服务器被占用的资源怎么释放?
  • 《深入Java包装类体系:类型转换原理与Integer缓存实战指南》
  • 基于IPD流程体系的研发项目计划管理
  • Go 开发环境配置完整指南
  • 如何将普通HTTP API接口改造为MCP服务器
  • Numpy科学计算与数据分析:Numpy数组属性入门之形状、维度与大小
  • Node.js特训专栏-实战进阶:21.Nginx反向代理配置
  • Spring MVC文件上传详解
  • 使用 Tauri 开发 Android 应用:环境搭建与入门指南
  • Android 之 面试八股文
  • MySQL GROUP BY 语句详细说明