Handler是个老生常谈的问题,我相信几乎全部的Android开发者都会使用Handler,那关于Handler还有什么好讲的吗?Handler若是仅仅是使用的话,确实没什么好讲的,可是Handler倒是一个几乎全部面试官都会问的问题,不一样的要求问的深度也不同,今天我就带你们学习一下关于Handler你所必需要掌握的知识。程序员
首先有四个对象咱们必需要了解一下Handler、Looper、ThreadLocal还有MessageQueue。面试
首先咱们要明白Handler消息机制是用来干吗的?Handler是把其余线程切换到Handler所在的线程,注意,这里不是UI线程。虽然咱们的大多数使用Handler的场景,都是咱们在子线程作了一下耗时的操做(IO或者数据库),当子线程执行完之后咱们可能须要更新UI,这个时候咱们用Handler来处理(sendMessage()或者post())就把子线程切换到了UI线程了。假如说,咱们如今有这么一个需求,线程A发个信息给线程B(线程A、线程B都不是主线程),这个时候咱们用Handler依然能够作,只须要在线程B中建立好Handler就能够了(关于如何在子线程建立Handler我会在下面详细说明)。因此,Handler并非把其余线程切换到主线程(UI线程),而是切换到它所在的线程,这一点必定要弄清楚。数据库
Handler消息机制里面最核心的类,消息轮询。Handler要想切换线程(或者说发送消息),必需要先得到当前线程的Looper,而后调用Looper.loop()方法把MessageQueue里面的message轮询出来"交"给Handler来处理,至于如何“交”给Handler的,下面我会经过源码带你们分析。缓存
ThreadLocal是Looper内部的一个,它虽然前面有个“Thread”,但其实它并非线程,它至关于一个线程内部的存储类。刚才在讲Looper的时候咱们说到,“Handler要想切换线程(或者说发送消息),必需要先得到当前线程的Looper”,那如何得到当前线程的Looper呢?正是经过ThreadLocal,它能够在不一样线程之间互不干扰地存储数据。ThreadLocal里面有一个内部静态类对象ThreadLocalMap,经过其set()和get()方法对数据对象进行保存和读取。数据结构
MessageQueue——消息队列,虽然它叫消息队列,但实际上的结构并非一个队列,而是一种单链表的数据结构,只是使用列队的形式对数据进场作添加或者移除。MessageQueue是在Looper被建立的时候由Looper生成的。同一线程下,Handler发送的全部message都会被加入到MessageQueue里面,当Message被loop()之后,就会从消息队列中移除。Message我没有单独拿出来,由于确实比较简单,就是消息自己,它能够携带两个int型参数,若是要传比较复杂的参数就用obj属性,what属性用来区分是哪一个Handler发送的信息。架构
Handler是把其余线程切换到它所在的线程,使用Handler发消息以前要先建立Looper对象,建立和读取Looper都须要使用ThreadLocal,它能够在不一样线程之间互不干扰地保存数据。在建立Looper对象的同时也把MessageQueue建立好了,Handler所发的message会被添加到MessageQueue对象里面,Looper.loop()方法之后会把MessageQueue上的Message轮询出来。链接这几个对象的还有一条很重要的线,那就是线程,记住,以上的过程都要保证Handler、Looper、MessageQueue在同一线程,这样,才能保证Handler的消息机制被正确使用。ide
注意:咱们这里是为了让你们更好地学习Handler,因此要在子线程建立Handler,现实使用场景,不多会在子线程建立Handleroop
在主线程(UI线程)建立Handler,我相信全部人都会post
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mainHandler = new Handler(); mainHandler.post(new Runnable() { @Override public void run() { Log.e("qige_test", "thread_name=" + Thread.currentThread().getName()); } }); }
咱们先按照主线程的方式在子线程建立一个Handler试一下,看看会有什么样的结果学习
new Thread(){ @Override public void run() { childHandler=new Handler(); childHandler.post(new Runnable() { @Override public void run() { Log.e("qige_test","thread_name="+Thread.currentThread().getName()); } }); } }.start();
没错,如图所示尚未建立Looper,那么如何建立Looper呢?图中有句话已经给出了答案——Looper.prepare(),同时为了让咱们发送的消息被轮询到,还必需要调用Looper.loop(); 因此在完整的在子线程建立Handler的代码以下:
new Thread(){ @Override public void run() { //建立Looper,同时建立MessageQueue Looper.prepare(); childHandler=new Handler(); childHandler.post(new Runnable() { @Override public void run() { Log.e("qige_test","thread_name="+Thread.currentThread().getName()); } }); //轮询 Looper.loop(); } }.start();
这样就能够在子线程里面建立Handler了,看到这里,你可能会问,为何我在主线程建立Handler的时候,没有调用Looper.prepare()和Looper.loop()?这个问题咱们先放一下,咱们先从源码角度把Handler消息机制分析一下。
先看一下建立Looper和MessageQueue的方法Looper.prepare()
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { //ThreadLocal来了 if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
这里就出现了我刚才说的ThreadLocal对象。Android规定每一个线程有且仅有一个Looper,因此为了防止不一样的线程之间的Looper相互影响,使用ThreadLocal对象来存储Looper。
再看一眼new Looper()的源码
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
MessageQueue也出来了,建立Looper的同时建立MessageQueue。
下面咱们看Handler.post()或者是Handler.sendMessage()他们本质是同样的,把消息加入到消息队列当中
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
post()或者sendMessagexxx()最终都会调用sendMessageAtTime(Message msg, long uptimeMillis)方法,咱们直接看这个方法的源码
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); }
都很简单,最后一步enqueueMessage(queue, msg, uptimeMillis);是把消息加入队列,我们再点开看一下
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { //把当前的Handler赋值给msg.target msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
关键要看的地方是 msg.target = this刚才讲Message的时候没有提,Message的target属性就是一个Handler对象,这里直接把当前的Handler对象赋值过去了。最后一行:queue.enqueueMessage(msg, uptimeMillis)是把message加入到消息队列里面,具体的实现我抓不到了,知道这句话是把message加入到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."); } **标注1** final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. ........................ ...................... **标注2** for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } .................................... ................................... try { **标注3** msg.target.dispatchMessage(msg); dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0; } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ........................................ ........................................ **标注4** msg.recycleUnchecked(); } }
看源码的时候注意看一下标注,一个个地来说;
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
这里面msg.callback是个Runnable对象,也就是当咱们使用handler.post(Runnable r)的方法的话,这时候就直接去调用Runnable对象里面的东西了,若是使用的是handler.sendMessagexxx(),就是去调用咱们重写的handleMessage(msg)方法了。
若是调用post()方法发送消息
mainHandler.post(new Runnable() { @Override public void run() { //这里会被调用 Log.e("qige_test","thread_name="+Thread.currentThread().getName()); } });
若是调用sendMessagexxx()
mainHandler.sendEmptyMessage(0); mainHandler=new Handler(){ @Override public void handleMessage(Message msg) { //这里会被调用 Log.e("qige_test","what="+msg.what); } };
咱们再回到上一级源码
以上基本就是一个完整的Handler消息机制的过程,我再带你们好好回顾一下:
1.Looper.prepare();//这一步建立了Looper和MessageQueue
2.handler.sendMessagexxxx(); // 把message添加到MessageQueue上
3.Looper.loop();//轮询消息
4.handler.dispatchMessage(msg);//处理消息
好了,到了该解决历史遗留问题的时候了,为何咱们在主线程建立handler不须要调用Looper.prepare()和Looper.loop()呢?
这是由于,主线程已经给咱们建立好了,在哪里建立好的呢?
在Java里面,有一个程序的主入口,就是静态main方法
public class Test { //程序入口 public static void main(String... args){ }
在Android里面呢,一样有这么一个main方法入口,它在ActivityThread类中。在咱们App启动过程当中,会调用到ActivityTread的main()方法(因为篇幅问题,该过程没有办法详细讲,后期会推文章单独说一下),下面咱们直接看ActivityTread的main()方法的源码
为了方便理解,仍是只看关键部分代码
public static void main(String[] args) { .... //建立Looper和MessageQueue对象,用于处理主线程的消息 Looper.prepareMainLooper(); //建立ActivityThread对象 ActivityThread thread = new ActivityThread(); //创建Binder通道 (建立新线程) thread.attach(false); //消息轮询 Looper.loop(); }
看到这里,我想你应该明白了为啥在主线程里建立Handler不须要调用Looper.prepare()和Looper.loop()方法了吧,由于在App启动的时候,ActivityThread已经给咱们建立好了。不过,须要注意的是,我刚才在讲Looper.loop()源码的时候说过这里面会有一个无限死循环,那么程序运行到这里岂不是要永远卡在这了呢,那咱们的Activity、Service都是咋执行的呢? 看关键的源码这一句thread.attach(false),注释上其实解释的很清楚,经过Binder建立了新的线程,在这个新的线程里面运行了Activity、Service等组件,而之因此Android为何采用这种方式,让咱们留在之后再说,你只须要知道,Looper.loop()不会形成卡顿,主线程已经给咱们建立好了Looper和MessageQueue对象就能够了。
开篇我就说过,Handler几乎全部人都会用,但仅仅会用是不够,要知其然更知其因此然。不少面试官愿意问Handler相关的问题,好好阅读这篇文章,它会让你在面试的时候事半功倍。
有些东西你不只要懂,并且要可以很好地表达出来,可以让面试官承认你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工做当中你压根不会用到它,可是你要知道它是什么东西。
最后文末我为你们准备了一套精品Android架构师教程,保证你学了之后保证薪资上升一个台阶。(如下是一小部分,获取更多其余精讲进阶架构视频资料能够加我wx:X1524478394 免费获取)
一下是今天给你们分享的一些独家干货:
①Android开发核心知识点笔记
②对标“阿里 P7” 40W+年薪企业资深架构师成长学习路线图
③面试精品集锦汇总
④全套体系化高级架构视频
Android精讲视频领取学习后更加是如虎添翼!进军BATJ大厂等(备战)!如今都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现现在市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破本身涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
上述【高清技术脑图】以及【配套的架构技术PDF】能够 加我wx:X1524478394 免费获取