Android必备知识点之消息传递机制Handler

1.最常见的使用场景

Android中常用Handler使用场景?

并不能在子线程中访问UI控件,否则会触发程序异常,这时候需要通过Handler将更新UI的操作切换到主线程进行

系统为什么不允许在子线程中访问UI呢?

UI控件不是线程安全的,多线程并发访问的时候可能会导致UI处于不可预期的状态,如果对UI控件的访问加线程锁,会降低UI线程的访问效率,另外就是逻辑变的复杂。

简要概括:

Handler运行需要底层MessageQueue 和 Looper支撑

MessageQueue:消息队列,内部存储一组消息,一对队列的形式对外提供插入和删除的工作。内部存储结构不是队列,而是单链表

Looper:消息循环,MessageQueue只是消息的存储单元,不能处理消息,Looper则无限循环查询消息,如果有新消息,则处理,否则一直等待。

ThreadLocal:并不是线程,它的作用是在每个线程中存储数据。

Handle创建的时候会才用当前线程的Looper来构造消息循环系统,Handler是怎么获得当前线程的呢?是因为使用了ThreadLocal,它可以在不同的线程中存储并提供数据,通过ThreadLocal可以轻松获取每个线程的Looper。

ThreadLocal详解

ThreadLocal是一个线程内部存储类,通过他可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据,对于其他线程则无法获取到数据。

1.使用场景:

①以线程为作用域并且不同的线程具有不同的数据副本。
②复杂逻辑下的对象传递,例如监听器的传递。通过参数传递或者静态全局变量,都有局限性

2.使用

ThreadLocal<T> mtL = new ThreadLocal<T>();
//设置
mtl.set("..");
//获取
mtl.get();

3.源码解析

public void set(T value) {
    Thread currentThrea = Thread.currentThread();
    Values values = values(currentThread);
    if(values == null) {
        values = initializeValues(currentThrea);
    }
    values.put(this,value);
}

通过valuse方法获取当前线程中的ThreadLocal数据,没有则初始化,然后再讲ThreadLocal的值进行存储,数组形式的存储,这里数组排列顺序:所有的线程有一个排序,这个排序,存储数据是当前线程排序下角标加一。具体就不深入了~

Looper详解
new Thread(new Runnable() {
        @Override
        public void run() {
            //为当前线程创建looper
            Looper.prepare();
            Handler handler = new Handler();
            //开启消息循环
            Looper.loop();
        }
    }).start();

①Looper.quit()和Looper.quitSafely()的区别

quit会直接退出Looper,而quitSafely只是设定一个退出标记,然后把消息队列中的已有的消息处理完毕才安全的退出。

解析:

①Looper.prepare()

此方法会调用Looper(boolea quitAllowed)创建Looper,

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

持有当前线程和MessageQueue;

②Looper.loop()

代码过于长就不粘贴了,大概解析一下

....
for(;;){
    Message msg = mQueue.next();
    if(msg == null) {
        return;
    }
    ...
    msg.target.dispatchMessage(msg);
    ...
}

loop方法是个死循环,唯一跳出的方式就是MessageQueue的next方法返回null,即当调用Looper.quit()和Looper.quitSafely(),next为空,消息队列退出。也就是说,如果Looper必须退出吗,否则一直循环。

处理消息:msg.target.dispatchMessage(msg);其中msg.target是发送这条消息的Handler的对象,也就是说,消息最终交给dispatchMessage方法处理

Handler的工作原理

handler.sendMessage(Message msg); –> sendMessageDelayed(Message msg,long 0); –> sendMessageAtTime(Message msg, long delaymillis) –> enqueueMessage(MessageQueue queue, Message msg, long delaymillis)

Handler发送消息是向消息队列中插入一条消息,MessageQueue的next方法就会返回这条消息给Looper,Looper收到消息后就开始处理了,最终消息由Looper交由Handler处理,即Handler的dispatchMessage方法会被调用,这时Handler就进入处理消息阶段了,调用handleCallBack(msg)

抽象方法

 /**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
Handler和Handler.Callback的handleMessage区别:

如果直接用Handler的handleMessage黄色警报,换成静态的就好了,原因:MessageQueue中的消息队列会一直持有对handler的引用,而作为内部类的handler会一直持有外部类的引用,就会导致外部类不能被GC回收。当我们发延时很长的msg时就容易出现泄漏。所以此处应该设置为static,然后Handler就会跟随类而不是跟随类的对象加载,也就不再持有外部类的对象引用。

handler.post(new Runnable(){
    @Override
    public void run() {
         mTestTV.setText("This is post");//更新UI
    }});

和handler.sendMessage(msg);作用相当

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值