关于Handler消息机制的理解

我尽量不打错别字,用词准确,不造成阅读障碍。

今天看到一篇文章,讲解handler的消息传递机制,感觉讲的很好,于是结合自己的理解,写一篇文章。

正常使用Handler的时候是在Activity和Fragment中new Handler();并重写handleMessage(Message msg);方法,然后在哪一个地方使用handler.sendEmptyMessage(msg);方法,发送Message,方法很多,简单举例。

我的理解:Handler的机制就像生活中的你乘坐大超市那种扶梯一样,有种超市的扶梯是有人的时候才会启动。正常扶梯是停止的,因为它检测到没有人,这时候你往扶梯上一站就会触发扶梯滚动,扶梯就是MessageQueue,人就是Message,检测扶梯上是否有人就是Looper的职责,控制你走上扶梯的大脑就是Handler。这个理解其实不全面,但是大致意思是清楚了。

其实在Activity创建运行的时候是有一个入口函数的,main(String[] args)函数,就是普通java的入口函数,如下:

public static void main(String[] args) { 
    ...... 
    Looper.prepareMainLooper();  //创建Looper
    ActivityThread thread = new ActivityThread(); 
    thread.attach(false); 
    if (sMainThreadHandler == null) { 
        sMainThreadHandler = thread.getHandler(); 
    } 
    Looper.loop();  //开启循环,从MessageQueue中取消息
    throw new RuntimeException("Main thread loop unexpectedly exited"); 
}

主要是在其中调用了Looper.prepareMainLooper();方法,这个方法是创建Looper的,相当于你创建了检测扶梯上是否有人的系统,在这个创建过程中Looper会将自己放入到一个ThreadLocal中,这是个类似于HashMap的类,可以存放数据,为什么要放进去?为了以后谁想获取Looper 的时候是直接从ThreadLocal里面获取就好了,类似于一个Looper的管理。

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 
private static void prepare(boolean quitAllowed) { 
    if (sThreadLocal.get() != null) { 
        throw new RuntimeException("Only one Looper may be created per thread"); 
    } 
    sThreadLocal.set(new Looper(quitAllowed)); 
}

多说一句:ThreadLocal是有其特殊性的,要不然为什么用它,存放数据的类辣么多,因为它是要在指定的线程中创建并放入数据,同时只能在那个指定的线程中取出数据,局限性很明显,但就是要这个局限性。举例:

ThreadLocal<Integer> mThreadLocal = new ThreadLocal<>(); 
private void testMethod() { 
    mThreadLocal.set(0); 
    Log.d(TAG, "main  mThreadLocal=" + mThreadLocal.get()); 
    new Thread("Thread1") { 
        @Override 
        public void run() { 
            mThreadLocal.set(1); 
            Log.d(TAG, "Thread1  mThreadLocal=" + mThreadLocal.get()); 
        } 
    }.start(); 
    new Thread("Thread2") { 
        @Override 
        public void run() { 
            mThreadLocal.set(2); 
            Log.d(TAG, "Thread2  mThreadLocal=" + mThreadLocal.get()); 
        } 
    }.start(); 
    Log.d(TAG, "main  mThreadLocal=" + mThreadLocal.get()); 
}

输出结果:

main  mThreadLocal=0 
Thread1  mThreadLocal=1 
Thread2  mThreadLocal=2 
main  mThreadLocal=0

这个效果其实也可以用Map实现,HashMap<Thread, Object>ConcurrentMap<Thread, Object>, 之所以不用,有一段话:
“使用Map会有一些麻烦的事要处理,比如当一个线程结束的时候我们如何删除这个线程的对象副本呢?如果使用ThreadLocal就不用有这个担心了,ThreadLocal保证每个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的并且 ThreadLocal 实例是可访问的;在线程消失之后,其线程局部实例的所有副本都会被垃圾回收(除非存在对这些副本的其他引用)。更多ThreadLocal的讲解参考:Android线程管理之ThreadLocal理解及应用场景,地址:https://www.cnblogs.com/whoislcj/p/5811989.html。”

获取looper比较简单,这个myLooper()方法后面会用到多次:

public static @Nullable Looper myLooper() { 
    return sThreadLocal.get(); 
}

创建了检测扶梯是否有人的系统后,这个时候MessageQueue已经存在了,因为Looper中含有MessageQueue,程序和生活还是有点不一样的。有了系统后,接下来就打开它,Looper.loop();,系统启动后就会检查电梯上是否有人:

public static void loop() { 
    final Looper me = myLooper(); 
    if (me == null) { 
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); 
    } 
    final MessageQueue queue = me.mQueue; 
    for (;;) { 
        Message msg = queue.next(); // might block 
        if (msg == null) { 
            // No message indicates that the message queue is quitting. 
            return; 
        } 
        try { 
            msg.target.dispatchMessage(msg); 
        } finally { 
            if (traceTag != 0) { 
                Trace.traceEnd(traceTag); 
            } 
        } 
        msg.recycleUnchecked(); 
    } 
}

里面的死循环就是检测是否有message(人)的地方,没有message(人),就不往下走,也就不会触发发送代码,电梯就不会启动。一旦有了message,就会触发msg.target.dispatchMessage(msg);方法,这个方法就会将message发送到你Handler中的handleMessage(Message msg)中,你就可以处理了。其中msg.tartget就是你创建的handler对象,这是在发送message的时候赋值的。

这个时候电梯已经准备好了,系统也工作了,而你其实一行代码都没有写,这都是Android为你准备的。现在你写了new Handler();构造方法其实做了很多事情:

public Handler(Callback callback, boolean async) { 
    if (FIND_POTENTIAL_LEAKS) { 
        final Class<? extends Handler> klass = getClass(); 
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && 
                (klass.getModifiers() & Modifier.STATIC) == 0) { 
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " + 
                klass.getCanonicalName()); 
        } 
    } 
    mLooper = Looper.myLooper(); 
    if (mLooper == null) { 
        throw new RuntimeException( 
            "Can't create handler inside thread that has not called Looper.prepare()"); 
    } 
    mQueue = mLooper.mQueue; 
    mCallback = callback; 
    mAsynchronous = async; 
}

首先要获取mLooper,就是从ThreadLocal里面取出Looper,然后从mLooper中拿出mQueue,这样就相当于,你找到了电梯的位置,但你上不上电梯就是看你自己了。

接下来你写了一句 handler.sendEmptyMessage(msg);

public final boolean sendMessage(Message msg) { 
    return sendMessageDelayed(msg, 0); 
} 

public final boolean sendMessageDelayed(Message msg, long delayMillis) { 
    if (delayMillis < 0) { 
        delayMillis = 0; 
    } 
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); 
} 

public boolean sendMessageAtTime(Message msg, long uptimeMillis) { 
    MessageQueue queue = mQueue; 
    if (queue == null) { 
        RuntimeException e = new RuntimeException( 
                this + " sendMessageAtTime() called with no mQueue"); 
        Log.w("Looper", e.getMessage(), e); 
        return false; 
    } 
    return enqueueMessage(queue, msg, uptimeMillis); 
} 

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { 
    msg.target = this; 
    if (mAsynchronous) { 
        msg.setAsynchronous(true); 
    } 
    return queue.enqueueMessage(msg, uptimeMillis); 
}

代码很清楚,会走到sendMessageAtTime(...);方法中,这样就将queue拿出来,然后走到enqueueMessage(...);中,这个时候msg.target = this;将handler赋值给msg.target,这样就可以使用msg.target.dispathMessage(msg);去发送消息,这样条件都创建好了,就等你往电梯上站了,最后将message放入queue中,你走上了电梯!looper(系统)检测到了有message(人)放入了MessageQueue(电梯),然后msg.target也是有值的,就可以顺利走完msg.target.dispathMessage(msg);方法,电梯启动,将message(人)发送到handleMessage(msg);方法中交给开发者处理;

理解可能有误,举例只是举个差不多的例子,欢迎指正;

参考文章:https://www.jianshu.com/p/6cc4d4b4676b

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值