一起谈谈-Handler

基础讲解

看了不少的技术大牛分享的博客,里面从各个侧重点讲解Handler,俗话说"乱花渐欲迷人眼,浅草才能没马蹄";所以我选择拜读一次源码,从源码找寻答案只用这样才能才能记一下它的机制;

注意: 由于代码的分析需要在某一段代码上有特殊讲解,我会将理解写在带片段中

纸上得来终觉浅 绝知此事要躬行.那我们就开始吧!

摘要

探究Handler我决定从以下几个方面
  • 案例列举
  • Handler的创建
  • Handler消息发送
  • Handler消息处理

1. 案例列举

在我们开发程序过程中肯定少不了使用Handler,所以我在此处就不做过多的解释了,直接上代码

private Handler mineHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {​​
            super.handleMessage(msg);
​            switch (msg.what) {
              case Flag:        //此处对msg.what做识别判断,用于处理不同的业务
              break;
            }
        }
    };
​
Message message = Message.obtain();
message.what = AIDL_ACTION;
message.obj = ERROR_INFO;​
mineHandler.sendMessage(message);​

2. Handler的创建

源码分析
//我们创建Handler的方式
​private Hanlder mineHandler=new Handler(){};
​//--------------------------------------------------
//源码的创建过程
public class Handler {​​​
   ...​
   ​public Handler() {
       this(null, false);
   }
​
   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());
           }
       }
       //获取looper对象​
       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;
   } 
   ...​​​​
​}​
2.1 此处我们可以看到在Handler过程中会从ThreadLocal中获取当前线程的Looper对象,当Looper为Null会抛出一个RuntimeException异常
2.1.1 为何我们在UI线程中使用Hanlder并没有创建Looper为什么会不报错呢?

因为早在ActivityThread中main方法函数就已经为我们创建出了Looper对象

public final class ActivityThread{
   public static void main(String[] args) {
    ...
    //获取当前线程的Looper​
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
 }
​​​}
2.1.2 如果我们在其他线程中使用Handler需要如何做呢?

由于在Handler创建过程中会将当前线程的Looper对象赋给成员变量mLooper,并会对其判Null,若当前线程中不存在Looper对象则会抛出一个RuntimeException异常,所以在其他线程使用Handler必须先初始化一个Looper对象,代码如下:

new Thread(){
       Handler handler=null;
       public void run(){
            //1.为当前线程创建Looper,并且会绑定到ThreadLocal中
            Looper.prepare();
            handler=new Handler();
            //2.启动消息队列
            Looper.loop();
       } ;
 }.start()

3. Handler消息发送

3.1基础用法

  • post(Runnable)
  • postDelayed(Runnable, long)
  • postAtTime(Runnable, long)
  • sendEmptyMessage(int)
  • sendMessage(Message)
这些方法是向MessageQueue消息队列中添加添加消息,通过上面方法中的传递参数我们可以看出,Handler发送的消息分为两种类型即:
  • Runnable类型
  • Message类型
事实上由post方式发出的Runnable对象最后都被封装成Message对象,接下来我们就从以上方法中找出两个有代表性的方法做源码讲解根据发送消息的类型在区分
3.1.1 post(Runnable)源码分析
public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
​
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
​​
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);
}​​
3.1.1.1.在这一部分代码中我们发现post函数方法中调用getPostMessage(r)将Runnable封装成了一个Message,最终的实现是在sendMessageAtTime函数方法中调用 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);
}
3.1.1.2.最终通过MessageQueue的实例queue调用方法函数enqueueMessage(msg, uptimeMillis)实现
boolean enqueueMessage(Message msg, long when) {
   
    synchronized (this) {
        //判断当前线程是否已关闭​
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            //​消息回收
            msg.recycle();
            return false;
        }
         msg.markInUse();
         msg.when = when;
         Message p = mMessages;
         boolean needWake;
​        //若此判断成功,说明队列中没有数据存在,我们将会把传进来的msg放入队列首部
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {  //判断失败,说明队列中已存在数据,则将传进来的msg插入到队列中
             //在队列中间插入,通常我们不需要醒来事件队列,除非有一个阻塞的队列和最早的消息存在于异步消息队列中。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
             //此处为一个死循环​
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }
        //是否需要唤醒
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}
3.1.1.3.通过一个for死循环遍历MessageQueue中的message,若当前msg.when小于MessageQueue中某一条message.when时,就将msg插入到MessageQueue队列中该条message的后面.至此实现向MessageQueue队列中添加消息
3.1.2 sendMessage(Message)源码分析
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);
}​​
​
我们发现最终的调用依然进入了sendMessageAtTime方法函数进入到MessageQueue类的enqueueMessage(msg, uptimeMillis)实现的往下就进行过多的分析,同上面的调用步骤一致
不单单是这两个方法的调用一致,以下方法的调用都一致的最后都进入到sendMessageAtTime方法函数进入到MessageQueue类的enqueueMessage(msg, uptimeMillis)实现的
  • post(Runnable)
  • postDelayed(Runnable, long)
  • postAtTime(Runnable, long)
  • sendEmptyMessage(int)
  • sendMessage(Message)

  • |---> sendMessageDelayed(Message, long)
  • |--- | ---> sendMessageAtTime(Message, long)

4. Handler消息处理

Handler处理的是MessageQueue队列中获取的Message,那么是谁从MessageQueue获取的Message呢?答案是肯定的是Looper,那Looper又是如何获取的呢我们接着分析源码

4.1. 在声明Handler的时候源码会获取当前线程Looper对象的实例,并且会关联MessageQueue
public Handler(Callback callback, boolean async) {
    ...
    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;
}
4.2. Looper.loop()开启消息队列的轮询
我们在其他线程创建Handler的时候会先创建一个Looper然后调用Looper中的静态函数loop()方法开启队列轮询
new Thread(){
       Handler handler=null;
       public void run(){
            //1.为当前线程创建Looper,并且会绑定到ThreadLocal中
            Looper.prepare();
            handler=new Handler();
            //2.启动消息队列
            Looper.loop();
       } ;
 }.start()
那为什么我们在UI线程创建了Handler之后不需要调用Looper的静态函数loop()方法开启队列轮询呢?
答案就在源码的ActvityThread类中
public static void main(String[] args) {
    ...
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();
    throw new RuntimeException("Main thread loop unexpectedly exited");
}
原来在ActivityThread创建之初,就已经为程序开启了消息队列轮询了,所以洒洒水啦! 我们的系统已经为你做好了一切前提配置了
4.3. 那么Looper.loop()究竟做了哪些事情呢? 让我们源码中寻找答案吧!
public static void loop() {
    //首先从ThreadLocal获取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;
    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();
    //上来就搞一个for死循环果然霸气十足​
    for (;;) {
        Message msg = queue.next(); // might block
        //若msg为Null,那就没必要再轮询了 直接return就够了​
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        //他的日志必须在本地变量中,以防UI事件设置日志记录器
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
        final long traceTag = me.mTraceTag;
        if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
            Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
        }
        final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        final long end;
        try {
            //上面解释了那么多,都是前期铺垫;这是我们的重点​
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
​
        if (slowDispatchThresholdMs > 0) {
            final long time = end - start;
            if (time > slowDispatchThresholdMs) {
                Slog.w(TAG, "Dispatch took " + time + "ms on "
                        + Thread.currentThread().getName() + ", h=" +
                        msg.target + " cb=" + msg.callback + " msg=" + msg.what);
            }
        }
        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();
    }
}
4.4. 经过了一段代码的分析,最终我们找到了分析重点 函数方法 -> msg.target.dispatchMessage(msg)
首先我们msg是Message实例对象,而target是Message中成员变量,那target是谁呢?
/*package*/ Handler target;​ 
原来它就是Handler啊,通过这种调用我们成功的将Handler与Looper关联到了一起
4.5.接下来我们就来分析msg.target.dispatchMessage(msg)方法吧 即Handler中的dispatchMessage(msg)
public void dispatchMessage(Message msg) {
    //优先处理msg中callback操作​
    if (msg.callback != null) {
        handleCallback(msg);
    } else { //若msg中无callback操作,执行此操作
        ​//再判断Handler是否有callback操作
        if (mCallback != null) {
            //若执行完操作返回的值为true,则return;
            //若返回false则继续执行handleMessage操作
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
       ​ //优先级最低的执行方法
        handleMessage(msg);
    }
}
4.5.1 handleCallback(msg);
private static void handleCallback(Message message) {
    message.callback.run();
}
4.5.2 mCallback.handleMessage(msg)
public interface Callback {
   /**
    * @param msg A {@link android.os.Message Message} object
    * @return True if no further handling is desired
    */
   public boolean handleMessage(Message msg);
}`
4.5.3 handleMessage(msg);
public void handleMessage(Message msg) {
}
4.5.5 总结

这三个方法都是在外部实现的,也就是由我们具体的业务决定的

至此我就谈完了对Handler消息的处理的理解!

汇总

  • handler创建时获取当前线程的lopper,并关联了looper的消息队列MessageQueue
  • handler通过post 或 sendMessage相关方法好说将消息发出,并添加到MessageQueue消息队列中
  • Looper通过调用loop方法函数,轮询MessageQueue不断地获取Message,由于Message中存在Handler的实例变量,通过此变量我们将消息回传到Handler中,经用户的实现抽象方法和接口完成对消息的处理!

This ALL! Thanks EveryBody!

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