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

相关推荐
ZSYP-S12 分钟前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos15 分钟前
c++------------------函数
开发语言·c++
程序员_三木27 分钟前
Three.js入门-Raycaster鼠标拾取详解与应用
开发语言·javascript·计算机外设·webgl·three.js
是小崔啊37 分钟前
开源轮子 - EasyExcel01(核心api)
java·开发语言·开源·excel·阿里巴巴
tianmu_sama43 分钟前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
黄公子学安全1 小时前
Java的基础概念(一)
java·开发语言·python
liwulin05061 小时前
【JAVA】Tesseract-OCR截图屏幕指定区域识别0.4.2
java·开发语言·ocr
jackiendsc1 小时前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
Oneforlove_twoforjob1 小时前
【Java基础面试题027】Java的StringBuilder是怎么实现的?
java·开发语言
羚羊角uou1 小时前
【C++】优先级队列以及仿函数
开发语言·c++