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.
相关推荐
rannn_11115 小时前
【苍穹外卖|Day3】公共字段自动填充、新增菜品功能、菜品分页查询功能、删除菜品功能、修改菜品功能、起售停售菜品
java·spring boot·后端·学习·项目
无名-CODING15 小时前
SpringMVC处理流程完全指南:从请求到响应的完整旅程
java·后端·spring
瑶山15 小时前
Spring Cloud微服务搭建三、分布式任务调度XXL-JOB
java·spring cloud·微服务·xxljob
Re.不晚15 小时前
深入底层理解HashMap——妙哉妙哉的结构!!
java·哈希算法
Serene_Dream15 小时前
Java 内存区域
java·jvm
俩个逗号。。15 小时前
修改Android resource dimens大小之后不生效
android
爱吃山竹的大肚肚16 小时前
文件上传大小超过服务器限制
java·数据库·spring boot·mysql·spring
黄昏恋慕黎明16 小时前
测试模型讲解
java
瑞雪兆丰年兮16 小时前
[从0开始学Java|第十二天]学生管理系统升级
java·开发语言
弹简特16 小时前
【JavaSE-网络部分03】网络原理-泛泛介绍各个层次
java·开发语言·网络