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.
相关推荐
月明长歌2 小时前
【码道初阶】Leetcode138:随机链表的复制:用 HashMap 做深拷贝的标准解法
java·数据结构·算法·leetcode·链表·哈希算法
消失的旧时光-19432 小时前
从 JVM 到 Linux:一次真正的系统级理解
android·linux·jvm
.简.简.单.单.2 小时前
Design Patterns In Modern C++ 中文版翻译 第八章 组合
java·c++·设计模式
习惯就好zz2 小时前
Android 12 RK3588平台电源菜单深度定制指南
android·rockchip·3588·电源按钮
nono牛2 小时前
Android.bp 语法编程指南 1
android
李坤林2 小时前
Android 12 BLASTBufferQueue 深度分析
android
感觉不怎么会2 小时前
Android13 - 网络模式默认 NR only(仅5G)
android·5g
盐焗西兰花2 小时前
鸿蒙学习实战之路-Core Vision Kit人脸检测实现指南
android·学习·harmonyos
码农搬砖_20202 小时前
【一站式学会compose】 Android UI体系之 Text的使用和介绍
android·compose