Android 消息机制

文章目录

  • [一. 概念](#一. 概念)
    • [1. ThreadLocal](#1. ThreadLocal)
    • [2. MessageQueue](#2. MessageQueue)
    • [3. Looper](#3. Looper)
    • [4. Handler原理](#4. Handler原理)
  • [二. 实例演示](#二. 实例演示)
    • [1. 整体使用方法](#1. 整体使用方法)
    • [2. 实例演示](#2. 实例演示)

一. 概念

Handler是Android消息机制的顶层接口,通过它能够将一个任务切换到Handler所在的线程中去执行,常用于更新UI。

主要包含了Handler,MessageQueue,Looper和ThreadLocal。

为什么要有这种功能?

Android出于安全问题,规定只能在主线程访问UI,并且建议耗时的操作不在主线程中进行。考虑如下情况,我要从服务端拉取很多信息并显示在UI上,这时候就产生了矛盾,因此需要Handler进行线程切换。

1. ThreadLocal

线程内部的数据存储类,只能在指定线程中获取到存储的数据,其他线程无法获取。在Handler中通过ThreadLocal来获取当前线程的Looper

代码演示:

kotlin 复制代码
         val mBooleanThreadLocal : ThreadLocal<Boolean> = ThreadLocal()
         mBooleanThreadLocal.set(true)
         Log.d("Thread",mBooleanThreadLocal.get().toString())
         Thread{
             mBooleanThreadLocal.set(false)
             Log.d("Thread",mBooleanThreadLocal.get().toString())
         }.start()
         Thread{
             Log.d("Thread",mBooleanThreadLocal.get().toString())
         }.start()
         //输出:说明三个线程各自的threadLocal都不一样
         //true
         //false
         //null

2. MessageQueue

即消息队列,底层实现不是队列,而是一个单链表的数据结构

插入方法:enqueMessage

读取方法:next,读取一条消息,并将其移除;如果没有消息,next方法会一直阻塞,直到有消息来

3. Looper

消息循环,即不停地从消息队列中查看是否有新消息,如果有就立刻处理,否则就一直阻塞在那里。

Looper的构造方法,会创建一个MessageQueue

java 复制代码
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

常用方法:

Looper.loop() 开启消息循环

Looper.prepare() 为当前线程创建一个Looper

prepareMainLooper()为主线程创建Looper

getMainLooper在任何地方都可以获取得到主线程的Looper

quit 直接退出Looper

quitSafely 设定一个退出标记,只有消息队列的已有消息处理完毕后才安全退出

kotlin 复制代码
        Thread{
            Looper.prepare()//
            val handler:Handler = Handler()
            Looper.loop()
        }.start()

4. Handler原理

分为消息发送和接收两部分

  • handler将消息发送到Looper的消息队列中(messageQueue),
  • messageQueue 将数据按照时间先后排好队,等待Looper.loop()按照先后顺序取出Message
  • Looper.loop()取出消息之后,调用消息的Message的target,即附属的Handler的dispatchMessage()方法,将该消息回调到handleMessage()方法中
  • handler 在 handleMessage(msg)方法中处理我们自己的逻辑。

    数量关系:
    一个Thread只能有一个Looper,可以有多个Handler,而一个Looper中又维护了一个MessageQueue队列

二. 实例演示

1. 整体使用方法

Handler的使用方式有两种:
Handler.sendMessage()

kotlin 复制代码
一,创建Handler子类
1、自定义Handler子类,并重写handleMessage()方法
    class mHandler extends Handler {
       //重写handleMessage()方法
         @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //执行的UI操作 
        }
    }
2、主线程中创建mHandler的实例
    private mHandler mhandler = new mHandler();
3、在子线程中创建需要发送的消息对象
    Message msg = Message.obtain();
    msg.what = 1;
4、在子线程中通过Hander发送消息到消息队列
    mhandler .sendMessage(msg);
5、启动子线程
 
二、使用匿名Handler子类
1、在主线程中通过匿名内部类创建Handler类对象
    Handler mhandler =  new Handler(){
        //重写handleMessage()方法
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //执行的UI操作 
        }
    };
2、在子线程中创建需要发送的消息对象
    Message msg = Message.obtain();
    msg.what = 1;
3、在子线程中通过Hander发送消息到消息队列
    mhandler .sendMessage(msg);
4、启动子线程

Handler.post()

kotlin 复制代码
1、在主线程中创建Handler实例
    private Handler mhandler = new Handler();
2、在子线程中使用Handler.post()
     mhandler.post(new Runnable() {
                                @Override
                            public void run() {
                                 //执行的UI操作 
                            }
                        });
3、启动子线程

2. 实例演示

  • 耗时子线程的使用
kotlin 复制代码
public class MainActivity extends AppCompatActivity {
 
    private static int MSG_1 = 0;
    private Handler handler = new Handler(Looper.myLooper()){
        //运行在主线程
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if(msg.what == MSG_1){
                textview.setText("Recv Msg:"+(String)msg.obj);
            }
        }
    };
    private Button btn_start;
    private TextView textview;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn_start = findViewById(R.id.btn_start);
        textview = findViewById(R.id.textView);
 
        btn_start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //模拟耗时任务,在子线程中完成
                new Thread(new Runnable() {
                    @Override
                    public void run() {//子线程任务
                        String buf = "abc123";
                        try {
                            Thread.sleep(6000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //handler send message
                        Message msg = new Message();
                        msg.what = MSG_1;
                        msg.obj = buf;
                        handler.sendMessage(msg);
                    }
                }).start();
                Toast.makeText(MainActivity.this,"main thread go on",Toast.LENGTH_SHORT);
            }
        });
    }
}

文献1
文献2

相关推荐
路边草随风21 小时前
milvus向量数据库使用尝试
人工智能·python·milvus
一笑的小酒馆21 小时前
Android CameraX适配Android15
android
hnlgzb21 小时前
安卓app开发,如何快速上手kotlin和compose的开发?
android·开发语言·kotlin
newobut21 小时前
vscode远程调试python程序,基于debugpy库
vscode·python·调试·debugpy
alexhilton21 小时前
Jetpack Compose 2025年12月版本新增功能
android·kotlin·android jetpack
思成不止于此21 小时前
【MySQL 零基础入门】DQL 核心语法(二):表条件查询与分组查询篇
android·数据库·笔记·学习·mysql
无敌最俊朗@1 天前
STL-deque面试剖析(面试复习4)
开发语言
APIshop1 天前
用 Python 把“API 接口”当数据源——从找口子到落库的全流程实战
开发语言·python
Java Fans1 天前
Qt Designer 和 PyQt 开发教程
开发语言·qt·pyqt
RwTo1 天前
【源码】-Java线程池ThreadPool
java·开发语言