Android Framework-Handler消息机制(二)

        在多线程通信中,Handler接收消息端的线程是独立的,无论Handler的引用在哪个线程发送消息,Handler在哪里实例化就在哪里接收消息。Handler拥有独立存在于线程内部且私有使用的Looper类来实现发送消息。

       总体来讲,当Handler对象在其他线程发送消息时,可通过Handler的引用对象将消息添加到MessageQueue 并找到其线程的Looper处理接收消息,Looper接收消息后再分发给Handler的接收消息方法。

接下来看看Handler家族的四大成员:

一、Handler----负责handler 消息发送,提供发送和接收消息的接口。

二、Looper----实现消息接收端存在且独立于线程内部,负责从消息队列中取消息分发给Handler接收消息端。

        Looper采用Java 提供的 ThreadLocal 来实现线程独立。ThreadLocal 支持泛型,在实例化对象时需重写initialValue 用于引用变量的初始化,默认为 null。主要方法:

         1.1、T get()  返回此线程局部变量的当前线程副本中的值(引用变量)。若变量没有当前线程的值,则先将其初始化为initialvalue()方法调用返回的值。

         1.2 void remove() 删除本地线程的引用变量

         1.3 set(T value)为本地线程引用变量赋值。也可通过 initialValue 来实现初始化赋值

ThreadLocal  |  Android Developersicon-default.png?t=N7T8https://developer.android.google.cn/reference/java/lang/ThreadLocal.html

Looper特点:

    1、职责单一,负责从消息队列中取消息,并分发给对应handler

    2、线程独立,且每个线程只能存在一个Looper

    3、会根据存活情况,创建和退出属于他自己的MessageQueue

创建和退出 Looper:

源码分析:

1、UI线程 Looper 实例   
private static Looper sMainLooper;
2、Worker 线程 Looper 实例,用 ThreadLocal 保存的对象都是线程独立的
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
3、与当前Looper对应的消息队列,被 final 关键字修饰的,只能被赋值一次
final MessageQueue mQueue;
4、当前Looper所在的线程,被 final 关键字修饰的,只能被赋值一次
final Thread mThread;
5、对外初始化方法:在普通线程中初始化 Looper 调用此方法。可调用该方法为自己的 worker 线程创建 Looper,但需注意只能初始化一次。
public static void prepare(){
    prepare(true);
}
6、对外初始化方法:UI 线程中初始化 Looper 调用此方法。prepareMainLooper在应用被创建就会调用。
    public static void prepareMainLooper() {
        // 因为是主线程,初始化一个不允许退出的 Looper
        prepare(false);
        synchronized (Looper.class) {
         // 如果 sMainLooper 不等于空说明已经创建过主线程 Looper 了,无需重复创建
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has
                        already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }    
7、私有初始化构造方法:@param quitAllowed 是否允许退出 Looper
 private static void prepare(boolean quitAllowed) {
        // 每个线程只能有一个 Looper
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per
                    thread");
        }
        // 保存实例
        sThreadLocal.set(new Looper(quitAllowed));
    }
8、私有构造方法:@param quitAllowed 是否允许退出 Looper。在构造方法中创建了对应的 MessageQueue 实例,然后得到当前线程
private Looper(boolean quitAllowed) {
        // 初始化 MessageQueue
        mQueue = new MessageQueue(quitAllowed);
        // 得到当前线程实例
        mThread = Thread.currentThread();
    }
9、调用 Looper.prepare()方法初始化完成后,可调用 myLooper()和 myQueue()方法得到当前线程对应的实例。
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
public static @NonNull MessageQueue myQueue() {
    return myLooper().mQueue;
}
10、退出Looper:安全和不安全

public void quit() {

         mQueue.quit(false);

}

public void quitSafely() {

         mQueue.quit(true);

}

10、运行Loopr处理消息

当 Looper.prepare()方法初始化完成,再调用Looper.loop()方法就可运行Looper 

public static void loop() {
// 得到当前线程下的 Looper
        final Looper me = myLooper();
// 如果还没初始化过抛异常
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
// 得到当前线程下与 Looper 对应的消息队列
        final MessageQueue queue = me.mQueue;
// 得到当前线程的唯一标识(uid+pid),作用是下面每次循环都判断一下线程有没有被切换
// 第一次调用 Binder.clearCallingIdentity()为了保存当前的调用者身份标识,将其保存到变量ident中;第二次调用 Binder.clearCallingIdentity()在操作完成后,恢复之前保存的调用者身份标识,以确保后续的操作可以继续在正确的身份标识下执行。
        Binder.clearCallingIdentity();//用于清除调用者的身份标识(identity)
        final long ident = Binder.clearCallingIdentity();
// 进入死循环不断取出消息
        for (;;) {
// 从队列中取出一个消息,这可能会阻塞线程
            Message msg = queue.next();
 // 如果消息是空的,说明队列已经退出了,直接结束循环,结束方法
            if (msg == null) {
                return;
            }
// 打印日志
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + " : " + msg.what);
            }
// 性能分析相关的东西
            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag,
                        msg.target.getTraceName(msg));
            }
            try {
//尝试将消息分发给宿主(Handler),dispatchMessage 为宿主 Handler 的接收消息方法
                msg.target.dispatchMessage(msg);
            } finally {
// 性能分析相关的东西
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
//打印日志
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " +
                        msg.callback);
            }
//得到当前线程的唯一标识
            final long newIdent = Binder.clearCallingIdentity();
//如果本次循环所在的线程与最开始不一样,打印日志记录
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                                + Long.toHexString(ident) + " to 0x"
                                + Long.toHexString(newIdent) + " while dispatching to "+ msg.target.getClass().getName() + " "
                                + msg.callback + " what=" + msg.what);
            }
//消息分发完毕,回收消息到缓存池
            msg.recycleUnchecked();
        }
    }

           总之,Looper 核心是调用 Looper.loop()不断的从消息队列中取出消息并分发给对应的 Handler。

三、Message----负责保存消息数据,规范消息传递格式。

四、MessageQueue----保存待处理消息队列,解决数据并发。

  • 35
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值