Android 消息机制 - Looper(Looper 静态方法、Looper 静态方法注意事项、Looper 实例方法、Looper 实例方法注意事项)

一、Looper 概述

  1. Looper 是 Android 消息机制的核心组件之一,它为线程创建并管理消息循环,让线程能够持续处理消息队列中的消息

  2. Looper 是实现主线程(UI 线程)消息循环、Handler 跨线程通信的基础


二、Looper 静态方法

1、prepareMainLooper 方法
java 复制代码
public static void prepareMainLooper()
  • prepareMainLooper 方法用于给主线程初始化 Looper,开发者禁止手动调用,否则抛出异常
2、getMainLooper 方法
(1)基本介绍
java 复制代码
public static Looper getMainLooper()
  • getMainLooper 方法用于在任意线程获取主线程 Looper,它是跨线程更新 UI 的核心
(2)演示
java 复制代码
Looper mainLooper = Looper.getMainLooper();
3、prepare 方法、myLooper 方法、loop 方法
(1)基本介绍
java 复制代码
public static void prepare()
  1. prepare 方法用于为当前线程创建 Looper(含 MessageQueue),一个线程只能调用一次
java 复制代码
public static Looper myLooper()
  1. myLooper 方法用于获取当前线程的 Looper 实例
java 复制代码
public static void loop()
  1. myLooper 方法用于启动消息循环,无限读取 MessageQueue 中的消息并分发,会阻塞当前线程,直到调用 quit 方法或者 quitSafely 方法
(2)演示
java 复制代码
public class MyLooperThread extends Thread {

    public static final String TAG = MyLooperThread.class.getSimpleName();

    private Handler handler;

    @Override
    public void run() {
        Log.i(TAG, "创建 Looper");
        Looper.prepare();

        handler = new Handler(Looper.myLooper()) {

            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.i(TAG, "处理消息:" + msg.what + " " + msg.obj);
            }
        };

        Log.i(TAG, "开启 Looper 循环");
        Looper.loop();

        Log.i(TAG, "终止 Looper 结束");
    }

    public Handler getHandler() {
        return handler;
    }
}
java 复制代码
MyLooperThread myLooperThread = new MyLooperThread();
myLooperThread.start();

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

Handler handler = myLooperThread.getHandler();

Message message = Message.obtain();
message.what = 100;
message.obj = "hello";
handler.sendMessage(message);

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

handler.getLooper().quitSafely();
复制代码
# 输出结果

创建 Looper
开启 Looper 循环
处理消息:100 hello
终止 Looper 结束
4、myQueue 方法
(1)基本介绍
java 复制代码
public static MessageQueue myQueue()
  • myQueue 方法用于获取当前线程的 Looper 实例关联的 MessageQueue 实例
(2)演示
java 复制代码
MessageQueue messageQueue = Looper.myQueue();

三、Looper 静态方法注意事项

  1. 在主线程中调用 prepareMainLooper 方法,会抛出异常
java 复制代码
Looper.prepareMainLooper();
复制代码
# 输出结果

FATAL EXCEPTION: main
Process: com.my.handler, PID: 8553
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my.handler/com.my.handler.MainActivity}: java.lang.RuntimeException: Only one Looper may be created per thread
  1. 一个线程只能调用一次 prepare 方法,否则会抛出异常
java 复制代码
public class TestThread extends Thread {

    public static final String TAG = TestThread.class.getSimpleName();

    private Handler handler;

    @Override
    public void run() {
        Log.i(TAG, "调用 Looper.prepare()");
        Looper.prepare();
        Log.i(TAG, "再次调用 Looper.myLooper()");
        Looper.prepare();

        handler = new Handler(Looper.myLooper()) {

            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.i(TAG, "处理消息:" + msg.what + " " + msg.obj);
            }
        };

        Log.i(TAG, "开启 Looper 循环");
        Looper.loop();

        Log.i(TAG, "终止 Looper 结束");
    }

    public Handler getHandler() {
        return handler;
    }
}
java 复制代码
TestThread testThread = new TestThread();
testThread.start();
复制代码
# 输出结果

FATAL EXCEPTION: Thread-2
Process: com.my.handler, PID: 26512
java.lang.RuntimeException: Only one Looper may be created per thread

四、Looper 实例方法

1、getThread 方法
(1)基本介绍
java 复制代码
public Thread getThread()
  • getThread 方法用于获取当前 Looper 绑定的线程
(2)演示
java 复制代码
Looper mainLooper = Looper.getMainLooper();

Thread thread = mainLooper.getThread();

Log.i(TAG, thread.getName());
复制代码
# 输出结果

main
2、getQueue 方法
(1)基本介绍
java 复制代码
public MessageQueue getQueue()
  • getQueue 方法用于获取当前 Looper 关联的 MessageQueue 实例
(2)演示
java 复制代码
Looper mainLooper = Looper.getMainLooper();

MessageQueue messageQueue = mainLooper.getQueue();
3、isCurrentThread 方法
(1)基本介绍
java 复制代码
public boolean isCurrentThread()
  • isCurrentThread 方法判断当前调用线程是否是 Looper 绑定的线程
(2)演示
java 复制代码
Looper mainLooper = Looper.getMainLooper();

Log.i(TAG, String.valueOf(mainLooper.isCurrentThread()));
复制代码
# 输出结果

true
4、dump 方法
(1)基本介绍
java 复制代码
public void dump(@NonNull Printer pw, @NonNull String prefix)
  • dump 方法用于将当前 Looper 的关键状态输出到指定流
(2)演示
java 复制代码
Looper mainLooper = Looper.getMainLooper();

Printer printer = new Printer() {

    @Override
    public void println(String x) {
        Log.i("LooperDump", x);
    }
};

mainLooper.dump(printer, "mainLooper: ");
复制代码
# 输出结果

mainLooper: Looper (main, tid 1) {2176923}
mainLooper:   Message 0: { when=-1s265ms what=159 obj=android.app.servertransaction.ClientTransaction@8382 target=android.app.ActivityThread$H }
mainLooper:   Message 1: { when=-853ms what=149 obj=android.os.BinderProxy@442a3a8 target=android.app.ActivityThread$H }
mainLooper:   Message 2: { when=-204ms callback=android.app.-$$Lambda$ActivityThread$A4ykhsPb8qV3ffTqpQDklHSMDJ0 target=android.app.ActivityThread$H }
mainLooper:   Message 3: { when=-70ms callback=androidx.core.content.res.ResourcesCompat$FontCallback$$ExternalSyntheticLambda0 target=android.os.Handler }
mainLooper:   Message 4: { when=+4s422ms what=1000 target=android.graphics.AwareBitmapCacher$MyHandler }
mainLooper:   (Total messages: 5, polling=false, quitting=false)
5、quitSafely 方法
(1)基本介绍
java 复制代码
public void quitSafely()
  • quitSafely 方法用于安全终止 Looper 消息循环,处理完已就绪消息后终止
(2)演示
java 复制代码
public class DelayThread extends Thread {

    public static final String TAG = DelayThread.class.getSimpleName();

    private Handler handler;

    @Override
    public void run() {
        Log.i(TAG, "创建 Looper");
        Looper.prepare();

        handler = new Handler(Looper.myLooper()) {

            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.i(TAG, "开始处理消息:" + msg.what + " " + msg.obj);
                try {
                    Thread.sleep(2 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.i(TAG, "消息处理完成");
            }
        };

        Log.i(TAG, "开启 Looper 循环");
        Looper.loop();

        Log.i(TAG, "终止 Looper 结束");
    }

    public Handler getHandler() {
        return handler;
    }
}
java 复制代码
DelayThread delayThread = new DelayThread();
delayThread.start();

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

Handler handler = delayThread.getHandler();

Message message1 = Message.obtain();
message1.what = 1;
message1.obj = "hello 1";
handler.sendMessage(message1);

Message message2 = Message.obtain();
message2.what = 2;
message2.obj = "hello 2";
handler.sendMessage(message2);

Message message3 = Message.obtain();
message3.what = 3;
message3.obj = "hello 3";
handler.sendMessage(message3);

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

handler.getLooper().quitSafely();
复制代码
# 输出结果

创建 Looper
开启 Looper 循环
开始处理消息:1 hello 1
消息处理完成
开始处理消息:2 hello 2
消息处理完成
开始处理消息:3 hello 3
消息处理完成
终止 Looper 结束
6、quit 方法
(1)基本介绍
java 复制代码
public void quit()
  • quit 方法用于立即终止 Looper 消息循环,清空消息队列
(2)演示
java 复制代码
public class DelayThread extends Thread {

    public static final String TAG = DelayThread.class.getSimpleName();

    private Handler handler;

    @Override
    public void run() {
        Log.i(TAG, "创建 Looper");
        Looper.prepare();

        handler = new Handler(Looper.myLooper()) {

            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.i(TAG, "开始处理消息:" + msg.what + " " + msg.obj);
                try {
                    Thread.sleep(2 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.i(TAG, "消息处理完成");
            }
        };

        Log.i(TAG, "开启 Looper 循环");
        Looper.loop();

        Log.i(TAG, "终止 Looper 结束");
    }

    public Handler getHandler() {
        return handler;
    }
}
java 复制代码
DelayThread delayThread = new DelayThread();
delayThread.start();

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

Handler handler = delayThread.getHandler();

Message message1 = Message.obtain();
message1.what = 1;
message1.obj = "hello 1";
handler.sendMessage(message1);

Message message2 = Message.obtain();
message2.what = 2;
message2.obj = "hello 2";
handler.sendMessage(message2);

Message message3 = Message.obtain();
message3.what = 3;
message3.obj = "hello 3";
handler.sendMessage(message3);

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

handler.getLooper().quit();
复制代码
# 输出结果

创建 Looper
开启 Looper 循环
开始处理消息:1 hello 1
消息处理完成
终止 Looper 结束
7、setMessageLogging 方法
(1)基本介绍
java 复制代码
public void setMessageLogging(@Nullable Printer printer)
  • setMessageLogging 方法用于监听消息分发全流程,为 Looper 实例设置一个 Printer ,Looper 会在消息分发前、分发后分别调用
(2)演示
java 复制代码
public class MyLooperThread extends Thread {

    public static final String TAG = MyLooperThread.class.getSimpleName();

    private Handler handler;

    @Override
    public void run() {
        Log.i(TAG, "创建 Looper");
        Looper.prepare();

        handler = new Handler(Looper.myLooper()) {

            @Override
            public void handleMessage(@NonNull Message msg) {
                Log.i(TAG, "处理消息:" + msg.what + " " + msg.obj);
            }
        };

        Log.i(TAG, "开启 Looper 循环");
        Looper.loop();

        Log.i(TAG, "终止 Looper 结束");
    }

    public Handler getHandler() {
        return handler;
    }
}
java 复制代码
MyLooperThread myLooperThread = new MyLooperThread();
myLooperThread.start();

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

Handler handler = myLooperThread.getHandler();

Looper looper = handler.getLooper();

looper.setMessageLogging(new Printer() {
    @Override
    public void println(String s) {
        Log.i("LooperMessage", s);
    }
});

Message message1 = Message.obtain();
message1.what = 1;
message1.obj = "hello 1";
handler.sendMessage(message1);

Message message2 = Message.obtain();
message2.what = 2;
message2.obj = "hello 2";
handler.sendMessage(message2);

Message message3 = Message.obtain();
message3.what = 3;
message3.obj = "hello 3";
handler.sendMessage(message3);

try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}

handler.getLooper().quit();
复制代码
# 输出结果

创建 Looper
开启 Looper 循环
>>>>> Dispatching to Handler (com.my.handler.MyLooperThread$1) {2176923} null: 1
处理消息:1 hello 1
<<<<< Finished to Handler (com.my.handler.MyLooperThread$1) {2176923} null
>>>>> Dispatching to Handler (com.my.handler.MyLooperThread$1) {2176923} null: 2
处理消息:2 hello 2
<<<<< Finished to Handler (com.my.handler.MyLooperThread$1) {2176923} null
>>>>> Dispatching to Handler (com.my.handler.MyLooperThread$1) {2176923} null: 3
处理消息:3 hello 3
<<<<< Finished to Handler (com.my.handler.MyLooperThread$1) {2176923} null
终止 Looper 结束

五、Looper 实例方法注意事项

  1. 在主线程中调用 quit 方法,会抛出异常
java 复制代码
Looper mainLooper = Looper.getMainLooper();

mainLooper.quit();
复制代码
# 输出结果

FATAL EXCEPTION: main
Process: com.my.handler, PID: 11860
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my.handler/com.my.handler.MainActivity}: java.lang.IllegalStateException: Main thread not allowed to quit.
  1. 在主线程中调用 quitSafely 方法,会抛出异常
java 复制代码
Looper mainLooper = Looper.getMainLooper();

mainLooper.quitSafely();
复制代码
# 输出结果

FATAL EXCEPTION: main
Process: com.my.handler, PID: 13300
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my.handler/com.my.handler.MainActivity}: java.lang.IllegalStateException: Main thread not allowed to quit.
相关推荐
驭渊的小故事4 小时前
MyBatis XML 开发实战:从配置到 CRUD 全解析
xml·java·mybatis
哦虎!9 小时前
【JavaEE】网络初识
java
for_ever_love__9 小时前
MySQL ORDER BY 排序优化:从 Using filesort 到索引有序扫描(含双路 / 单路排序原理)
java·mysql·优化·存储·by·order·filesort
我是人✓10 小时前
Java文件上传三方式对比:从传统到SpringBoot
java·开发语言
欣栀♡10 小时前
JVM 频繁 Full GC 先看日志括号里的 cause,jstat 的 FGC 在 JDK 8 上会骗人
java·jvm
IT研究室10 小时前
最新计算机毕业设计选题推荐-基于spring boot的葡萄酒文化宣传网站-网站-文档指导-Java-springboot
java·spring boot·课程设计
慧都小项10 小时前
MyEclipse 中创建和测试 JAX-RS REST 接口:从项目配置到 REST Explorer
java·restful·myeclipse·jax-rs
Wang's Blog10 小时前
Java 项目实战: 交友项目 - 入门案例:编写 Dubbo 服务提供者
java·dubbo·交友
秋饼11 小时前
Spring AI 2.0 结构化输出企业级实战:从 BeanOutputConverter 源码到生产级解析容错
java·ai·技术分享·后端开发
摇滚侠11 小时前
《计算机是如何工作的:人人都能懂的计算机软硬件工作原理》第 9 章 机器码与汇编语言 阅读笔记 9
java·开发语言·笔记