Handler机制原理

1.应用场景(什么情况下用到Handler机制)

倒计时(用Handler可以,并不是Handler机制)

网络请求(异步):

子线程获取数据交给主线程更新UI的时候,(子线程不能更新UI,如果更新,报错)

主线程不能直接网络请求

在后台执行耗时操作需要Service,

在Service中进行耗时操作,如果是HttpURLConnection则不行,需要自己new一个子线程,Retrofit和OKHttp可以,因为这两个里面自带异步,

同步和异步

1.2后台下载文件Service需要开启子线程进行网络请求下载文件,提示用户下载成功的时候,不能在子线程里面直接Toast,Handler发送消息给主线程直接在Service)

3.BroadcastReceiver(广播) 也是默认运行在主线程

Handler中重要的几个类:

Handler:

句柄 作用1:将子线程中的Message消息发送给主线程

  作用2:可以将主线程Message消息发送到子线程

  作用3:处理Message消息

Message:

消息对象,存储数据    

Message msg= new  Message();  每次需要的时候就带创建对象,浪费资源

Message msg =handler.obtainMessage(); 消息池可以复用Message对象(比较节省资源)

MessageQueue:

(Activity任务栈,先进后出原则,)

先进先出原则

每个线程都有一个MessageQueue对象,主线程在创建的时候会默认创建一个MessageQueue对象,      {子线程通过new的方式}  同时与之对应的还有个looper对象

Looper:

每一个线程都有一个Looper对象

作用:将MessageQueue里面的消息轮询出去交给Handler处理Looper.loop()死循环,会不停的从MessageQueue里面取消息,有消息就取出,没消息MessageQueue消息队列就处于阻塞状态,loop()就跳出死循环.

状态: 子线程获取Looper对象的话需要通过


Handler它是Android线程间通信工具类。

一般用于子线程向主线程发送消息,将子线程中执行结果通知主线程,从而在主线程中执行UI更新操作。

源码角度理解

Handler负责发送(sendMessage)和处理消息(handleMessage)

1)Message:消息载体,包含消息id(what)、消息处理对象(obj)、Runnable接口等。Handler发送一条消息后,会将该消息加入到MessageQueue统一消息处理队列中。

2)MessageQueue:消息队列。用来存放Handler发送的消息的队列,单链表实现

3)Looper:消息泵,通过Looper.loop( )创建一个死循环,不断地从MessageQueue中抽取Message,Message通过绑定的内部target(handler类型),msg.target.dispatchMessage(msg)将该消息传给对应的Handler接收。在dispatchMessage方法内调用handleCallback或handleMessage方法

ThreadLocal理解

创建Handler对象时,需要先为这个Handler创建Looper.prepara( )新建一个looper对象。一个线程中只会有一个looper。因为looper使用ThreadLocal.set方法。在set方法中,会对应着当前Thread,将Looper存储在其成员变量ThreadLocalMap中。其中key是ThreadLocal,value是looper。因此looper-threadLocal-Thread是一一对应关系。

实际上ThreadLocal的值是放入了当前线程的一个ThreadLocalMap实例中,所以只能在本线程中访问,其他线程无法访问。

对象存放在哪里

在Java中,栈内存归属于单个线程,每个线程都会有一个栈内存,其存储的变量只能在其所属线程中可见,即栈内存可以理解成线程的私有内存。而堆内存中的对象对所有线程可见。堆内存中的对象可以被所有线程访问。

Handler发送和处理消息过程分析

Handler发送消息:Handler.sendMessage(Message message)

Handler处理信息:

new  Handler(){

        // 通过复写handlerMessage()从而确定更新UI的操作

        @Override

        public void handleMessage(Message msg) {

                ...// 需执行的UI操作

            }

    };

sendMessage方法将message 入队messageQueue(单链表实现,接着说数据结构链表)在ActivityThread中会调用Looper.prepareMainLooper()生成looper,looper中会创建MessageQueue 。Looper.loop( )中有个死循环,不断从messageQueue中取message。

public static void main(String[] args) {

    Looper.prepareMainLooper();

    long startSeq = 0;

    if (args != null) {

        for (int i = args.length - 1; i >= 0; --i) {

            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {

                startSeq = Long.parseLong(

                        args[i].substring(PROC_START_SEQ_IDENT.length()));

            }

        }

    }

    ActivityThread thread = new ActivityThread();

    thread.attach(false, startSeq);


    if (sMainThreadHandler == null) {

        sMainThreadHandler = thread.getHandler();

    }

    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");

}

Looper类

private Looper(boolean quitAllowed) {

    mQueue = new MessageQueue(quitAllowed);

    mThread = Thread.currentThread();

}


public static void loop() {


        ...// 仅贴出关键代码


        // 1. 获取当前Looper的消息队列

            final Looper me = myLooper();         

            final MessageQueue queue = me.mQueue;

            for (;;) {  

            Message msg = queue.next();

            if (msg == null) {

                return;

            }

            // next():取出消息队列里的消息

            // 若取出的消息为空,则线程阻塞


            // 2.2 派发消息到对应的Handler

            msg.target.dispatchMessage(msg);

            // 把消息Message派发给消息对象msg的target属性

            // target属性实际是1个handler对象

        // 3. 释放消息占据的资源

        msg.recycle();

        }

}

Handler中的dispatchMessage方法

public void dispatchMessage(Message msg) {

    if (msg.callback != null) {

        handleCallback(msg);

    } else {

        if (mCallback != null) {

            if (mCallback.handleMessage(msg)) {

                return;

            }

        }

        handleMessage(msg);

    }

}





Handler原理源码分析


/**

Handler:发送接收处理对象

Looper: 每一个线程只有一个looper.它是线程持有的looper中有一个Looper.loop()方法去读取MessageQueue中的消息,读出来之后会交给Handler来进行消息的处理

Message:是Handler接收和处理的对象,

MessageQueue:是用对应线程的Looper来创建和管理的

**/


Handler负责发送(sendMessage)和处理消息(handleMessage)

Message:消息载体,包含消息id(what)、消息处理对象(obj)、Runnable接口等。Handler发送一条消息后,会将该消息加入到MessageQueue统一消息处理队列中。

MessageQueue:消息队列。用来存放Handler发送的消息的队列,单链表实现

Looper:消息泵,通过Looper.loop( )创建一个死循环,不断地从MessageQueue中抽取Message,Message通过绑定的内部target(handler类型),msg.target.dispatchMessage(msg)将该消息传给对应的Handler接收。在dispatchMessage方法内调用handleCallback或handleMessage方法



原理流程:

首先子线程获取数据之后会通过Message.obtain()封装成Message对象,然后主线程创建好的Handler会将Message发送到主线程的MessageQueue中,在创建Handler的时候内部构造还会创建一个Looper对象,创建Looper对象的同时又会创建一个MessageQueue队列这个对象,Looper会通过Looper.Loop()方法会不断的轮询MessageQueue中的Message交给Handler处理


源码分析:

Handler源码


在创建Handler的时候,它构造内部会通过Looper.mylooper()创建一个Looper对象,除此之外,还会通过looper创建一个MessageQueue对象然后赋值给全局的MessageQueue对象,这样Handler和Looper就共用一个MessageQueue,这样这三者也就捆绑在一起了(如上图)


Handler源码


HandLer在发送消息的时候,不管是通过sendEmptyMessage()还是通过sendEmptyMessageDelayed()发送消息,最后消息都会走到enqueueMessage()方法中,这个enqueueMessage()方法其实就是将Handler发送的消息添加到MessageQueue消息队列中(如上图)



MessageQueue源码


而这个MessageQueue中主要包含,插入,读取和删除操作,在MessageQueue中有个enqueueMessage()方法,这个方法就是向MessageQueue中插入消息,而MessageQueue中会通过个next()方法获取下个Message,会被Looper.Loop()方法不停的取出Message对象,MessageQueue底层其实是通过单链表的数据结构来维护Message对象列表的(如上图)



Looper源码



public static void loop() {


        ...// 仅贴出关键代码


        // 1. 获取当前Looper的消息队列

            final Looper me = myLooper();         

            final MessageQueue queue = me.mQueue;

            for (;;) {  

            Message msg = queue.next();

            if (msg == null) {

                return;

            }

            msg.target.dispatchMessage(msg);

        msg.recycle();

        }

}


Handler源码中的dispathMessage()和HandleMessage()


MessageQueue是通过当前线程的Looper对象来管理和创建的,在Looper的构造中会创建个MessageQueue对象(如上图Looper源码),另外Looper中通过Prerare()方法来创建当前线程的Looper对象,在prepare()方法内部其实是通过ThreadLocal来保存和获取这个Looper对象的,ThreadLocal 不是 Thread,它是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,对数据存储后,只有在线程中才可以获取到存储的数据,对于其他线程来说是无法获取到数据。也就是说每个 Thread内可以通过ThreadLocal来创建变量的实例副本,且该副本只能由当前 Thread 可以使用,所以Threadlocal可以确保每个线程有唯一的一个Looper对象,Looper类中还有个loop()方法,其实内部是个死循环,在死循环内部通过queue.next()不停的从MessageQueue中轮询消息,如果没消息的话MessageQueue就处于阻塞状态,但是对当前主线程没影响,轮询出来消息之后会将Message交给Handler中的dispatchMessage()来处理,最后把Message交给在dispatchMessage()中的HandleMessage来处理。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,390评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,821评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,632评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,170评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,033评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,098评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,511评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,204评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,479评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,572评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,341评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,893评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,171评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,486评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,676评论 2 335

推荐阅读更多精彩内容