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.
相关推荐
figo10tf7 小时前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
zhangyi_viva7 小时前
Spring Boot(七):Swagger 接口文档
java·spring boot·后端
橙露7 小时前
Spring Boot 核心原理:自动配置机制与自定义 Starter 开发
java·数据库·spring boot
doupoa7 小时前
内存指针是什么?为什么指针还要有偏移量?
android·c++
小程故事多_807 小时前
Agent Infra核心技术解析:Sandbox sandbox技术原理、选型逻辑与主流方案全景
java·开发语言·人工智能·aigc
冰暮流星7 小时前
sql语言之分组语句group by
java·数据库·sql
望舒5137 小时前
代码随想录day25,回溯算法part4
java·数据结构·算法·leetcode
黎雁·泠崖7 小时前
【魔法森林冒险】3/14 Allen类(一):主角核心属性与初始化
java·开发语言
黎雁·泠崖7 小时前
【魔法森林冒险】1/14 项目总览:用Java打造你的第一个回合制冒险游戏
java·开发语言
NuageL8 小时前
原始Json字符串转化为Java对象列表/把中文键名变成英文键名
java·spring boot·json