安卓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;

    复制代码
     @Override
     public void run() {
         // 1. 准备Looper
         Looper.prepare();
         
         // 2. 创建Handler(自动绑定当前线程的Looper)
         handler = new Handler() {
             @Override
             public void handleMessage(Message msg) {
                 // 处理消息
                 switch (msg.what) {
                     case 1:
                         // 处理消息类型1
                         break;
                 }
             }
         };
         
         // 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() {
    @Override
    public void handleMessage(Message msg) {
        // 处理消息
    }
};

Handler线程间通信

  1. 子线程 → 主线程

    public class MainActivity extends AppCompatActivity {
    private Handler mainHandler; // 绑定主线程的Looper

    复制代码
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         
         // 在主线程创建Handler,自动绑定主线程的Looper
         mainHandler = new Handler() {
             @Override
             public void handleMessage(Message msg) {
                 // 这个代码在主线程执行
                 updateUI();
             }
         };
         
         // 启动子线程
         new Thread(() -> {
             // 子线程执行耗时操作
             String result = doHeavyWork();
             
             // 发送消息到主线程
             mainHandler.post(() -> {
                 // 这个代码在主线程执行
                 updateUI(result);
             });
         }).start();
     }

    }

  2. 主线程 → 子线程

    public class WorkerThread extends Thread {
    private Handler workerHandler; // 绑定子线程的Looper

    复制代码
     @Override
     public void run() {
         // 1. 准备子线程的Looper
         Looper.prepare();
         
         // 2. 创建Handler,绑定到当前子线程的Looper
         workerHandler = new Handler() {
             @Override
             public 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;

    复制代码
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         
         // 启动工作线程
         workerThread = new WorkerThread();
         workerThread.start();
         
         // 主线程发送任务给子线程
         findViewById(R.id.button).setOnClickListener(v -> {
             workerThread.sendTask("处理这个任务");
         });
     }

    }

  3. 子线程 → 子线程

    public class ThreadA extends Thread {
    private Handler threadBHandler; // 绑定到线程B的Handler

    复制代码
     public ThreadA(Handler threadBHandler) {
         this.threadBHandler = threadBHandler;
     }
     
     @Override
     public void run() {
         // 线程A的工作
         while (true) {
             // 做一些工作
             String result = doWork();
             
             // 发送结果给线程B
             threadBHandler.post(() -> {
                 // 这个代码在线程B中执行
                 processResult(result);
             });
             
             Thread.sleep(1000);
         }
     }

    }

    public class ThreadB extends Thread {
    private Handler threadBHandler;

    复制代码
     @Override
     public void run() {
         Looper.prepare();
         
         // 创建Handler,绑定到线程B
         threadBHandler = new Handler() {
             @Override
             public void handleMessage(Message msg) {
                 // 在线程B中处理消息
                 System.out.println("线程B收到消息: " + msg.obj);
             }
         };
         
         Looper.loop();
     }
     
     public Handler getHandler() {
         return threadBHandler;
     }

    }

    // 使用示例
    public class ThreadCommunicationExample {
    public void startThreads() {
    // 启动线程B
    ThreadB threadB = new ThreadB();
    threadB.start();

    复制代码
         // 等待线程B的Handler准备就绪
         while (threadB.getHandler() == null) {
             Thread.sleep(10);
         }
         
         // 启动线程A,并传入线程B的Handler
         ThreadA threadA = new ThreadA(threadB.getHandler());
         threadA.start();
     }

    }

  4. 多线程Handler通信架构

    public class MultiThreadHandler {
    private Handler mainHandler; // 主线程Handler
    private Handler networkHandler; // 网络线程Handler
    private Handler dbHandler; // 数据库线程Handler

    复制代码
     public MultiThreadHandler() {
         // 主线程Handler
         mainHandler = new Handler(Looper.getMainLooper());
         
         // 启动网络线程
         startNetworkThread();
         
         // 启动数据库线程
         startDatabaseThread();
     }
     
     private void startNetworkThread() {
         new Thread(() -> {
             Looper.prepare();
             networkHandler = new Handler() {
                 @Override
                 public 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() {
                 @Override
                 public 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());
相关推荐
2501_9179700325 分钟前
主播生活模拟器2|主播人生模拟器2 (Streamer Life Simulator 2)免安装中文版
java·游戏·生活
破刺不会编程26 分钟前
linux信号量和日志
java·linux·运维·前端·算法
0wioiw033 分钟前
Android-Kotlin基础(Jetpack③-LiveData)
android·开发语言·kotlin
xzkyd outpaper39 分钟前
Android中Binder缓冲区为什么限制1MB,此外Bundle数据为什么要存储在Binder缓冲区中
android·binder
aqi001 小时前
FFmpeg开发笔记(七十九)专注于视频弹幕功能的国产弹弹播放器
android·ffmpeg·音视频·直播·流媒体
回家路上绕了弯2 小时前
线程池优化实战:从性能瓶颈到极致性能的演进之路
java·后端
小苏兮2 小时前
飞算JavaAI深度解析:专为Java生态而生的智能引擎
java·开发语言·人工智能·java开发·飞算javaai炫技赛
深盾科技3 小时前
Android 安全编程:Kotlin 如何从语言层保障安全性
android·安全·kotlin
whysqwhw3 小时前
RecyclerView的LayoutManager扩展用法
android
whysqwhw3 小时前
RecyclerView的全面讲解
android