Handler总结

  • 我们看下基本的概念
  • messagequeue===中文的翻译是消息队列,它的内部存储了一组消息,以队列的形式对外提供增加和删除的方法,但是他的内部不是队列的结构形式,而是采用单链表的数据结构来存储消息列表
  • looper==轮询器,也可以理解为消息循环,由于messagequeue是一个消息容器,所以他负责不断的去轮训消息,如果有消息处理的话就去处理消息,否则就一直等待着。他还有一个特殊的概念threadlocal
    ,并不是线程,他的作用是在每个线程中存储数据,负责绑定当前的线程的loop
  • 首先我们知道创建一个handel

    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());

                }

            //获取loper对象,如果lopper对象为空就会报异常,这就是为什么我们在子线程使用需要主动的调用 Looper.prepare();

            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;

        }

        

       我们看下Looper.prepare();的源码可以得知,只有sThreadLocal为空的时候起创建了一个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));

    }

    

    那么为什么我们在主线程就没有调用就没事呢!

    其实你在创建activitythred的内部就给你调用了Looper.prepareMainLooper()调用了Looper.prepare()方法,因此我们应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了。

  • 然后我们发送消息需要什么?

      messag对message,我们看看怎么创建一个message,那么创建message有那些方式,2中,我们一般推荐使用  Message.obtain();为什么推荐这个我么你看下源码!
    
      //我们可以清楚,首先他会看Message对象是不是已经存在了,如果存在了就直接复用,没有直接创建,这样原因不说也明白了吧。
    
      `  public static Message obtain() {
    
      synchronized (sPoolSync) {
    
          if (sPool != null) {
    
              Message m = sPool;
    
              sPool = m.next;
    
              m.next = null;
    
              m.flags = 0; // clear in-use flag
    
              sPoolSize--;
    
              return m;
    
          }
    
      }
    
      return new Message();
    

    }`

  • 有了消息我们就要发送出去,如何发送呢!
    `handler.sendMessage(message);`,这样我们就可以把消息发送出去。那么它里面是如何发送消息的,我们通过看源码得知他最终回调用sendMessageAtTime(),我们看下源码

    //其中msg参数就是我们发送的Message对象,而uptimeMillis参数则表示发送消息的时间,它的值等于自系统开机到当前时间的毫秒数再加上延迟时间

     public boolean sendMessageAtTime(Message msg, long uptimeMillis) {

        //如果quer为空就会停止轮训消息

        MessageQueue queue = mQueue;

        if (queue == null) {

            RuntimeException e = new RuntimeException(

                    this + " sendMessageAtTime() called with no mQueue");

            Log.w("Looper", e.getMessage(), e);

            return false;

        }

        //进入enqueueMessage

        return enqueueMessage(queue, msg, uptimeMillis);

    }

    //通过看源码我们知道,MessageQueue是在调用looper的时候创建的,因此一个Looper也就对应了一个MessageQueue。

    private Looper(boolean quitAllowed) {

        mQueue = new MessageQueue(quitAllowed);

        mThread = Thread.currentThread();

    }

    

    

    

    

    

    我们看下enqueueMessage的方法,在这里进行消息的入队,将消息插入到messagequene

        final boolean enqueueMessage(Message msg, long when) {

        if (msg.when != 0) {

            throw new AndroidRuntimeException(msg + " This message is already in use.");

        }

        //如果为空将报异常,msg.target就是handel

        if (msg.target == null && !mQuitAllowed) {

            throw new RuntimeException("Main thread not allowed to quit");

        }

        synchronized (this) {

            //如果调用了quit就会赋值为true

            if (mQuiting) {

                RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");

                Log.w("MessageQueue", e.getMessage(), e);

                return false;

            } else if (msg.target == null) {

                mQuiting = true;

            }

            msg.when = when;

            Message p = mMessages;

            //在这里他们将按照时间的顺序排列

            if (p == null || when == 0 || when < p.when) {

                msg.next = p;

                mMessages = msg;

                this.notify();

            } else {

                Message prev = null;

                while (p != null && p.when <= when) {

                    prev = p;

                    p = p.next;

                }

                msg.next = prev.next;

                prev.next = msg;

                this.notify();

            }

        }

           if (needWake) {

                //在native层进行消息的传递

                nativeWake(mPtr);

            }

        return true;

    }

  • 接下来我们看下消息的轮训 loop.loop()

    public static final void loop() {

    //获取Looper

    Looper me = myLooper();

    MessageQueue queue = me.mQueue;

    while (true) {

        //这里面也是个死循环去取消息如果当前MessageQueue中存在mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。
      //MessageQueue中的nex方法,去轮训消息内部是一个无限的循环轮询,这里面要想退出这个无限的循环只有queue.next()
      返回null,而要让他返回null,通过看源码mQuitting就行了,也就是说只要调用quit就可以了
        Message msg = queue.next(); // might block

        if (msg != null) {

        //如果handel为空将直接退出循环

            if (msg.target == null) {

                return;

            }

            if (me.mLogging!= null) me.mLogging.println(

                    ">>>>> Dispatching to " + msg.target + " "

                    + msg.callback + ": " + msg.what

                    );

            //这里就会调用handeld的dispatchMessage进行消息的分发,为什么是msg.target是handel通过源码我们看到enqueueMessage里面进行的赋值 msg.target = this;我们看下怎么分发的

            msg.target.dispatchMessage(msg);

            if (me.mLogging!= null) me.mLogging.println(

                    "<<<<< Finished to    " + msg.target + " "

                    + msg.callback);

            msg.recycle();

        }

    }

}





//我们进入到这个方法

public void dispatchMessage(Message msg) {

    if (msg.callback != null) {
       //这个方法熟悉把就把消息传递到我们重写的主线程的方法了,在这里进行回调handleMessage
        handleCallback(msg);

    } else {

        if (mCallback != null) {

            if (mCallback.handleMessage(msg)) {

                return;

            }

        }



        handleMessage(msg);

    }

}



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

推荐阅读更多精彩内容