Android 消息机制

一. 概念

Handler是Android消息机制的顶层接口,通过它能够将一个任务切换到Handler所在的线程中去执行,常用于更新UI。
主要包含了Handler,MessageQueue,Looper和ThreadLocal。
为什么要有这种功能?
Android出于安全问题,规定只能在主线程访问UI,并且建议耗时的操作不在主线程中进行。考虑如下情况,我要从服务端拉取很多信息并显示在UI上,这时候就产生了矛盾,因此需要Handler进行线程切换。
在这里插入图片描述

1. ThreadLocal

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

         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

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

常用方法:
Looper.loop() 开启消息循环
Looper.prepare() 为当前线程创建一个Looper
prepareMainLooper()为主线程创建Looper
getMainLooper在任何地方都可以获取得到主线程的Looper
quit 直接退出Looper
quitSafely 设定一个退出标记,只有消息队列的已有消息处理完毕后才安全退出

        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()

一,创建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()

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

2. 实例演示

  • 耗时子线程的使用
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

  • 19
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值