Handler原理

Handler简单使用

1.使用静态内部类的方式继承Handler并重写接受的方法handleMessage。之所以使用静态内部类,是因为静态内部类不会持有外部类的引用
static class MyHandler extends Handler{
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what){
                case 0x11:
                    String str = (String) msg.obj;
                    Log.d(TAG,"msg content == "+str);
                    break;
                default:break;
            }
        }
    }
2.获取Handler实例
private  Handler mHander = new MyHandler();
3.获取Message对象
1)Message msg = new Message();
2)Message msg = Message.obtain();/mHander.obtainMessage();等
obtain.what = 0x11;
obtain.obj = "Message Content";

获取Message方式有2中,建议使用第二种方式,因为第二种方法采用了缓存池机制,避免重复创建新的对象。

4.发送消息
mHander.sendMessage(msg);立即发出
mHander.sendMessageDelayed(obtain,time);延时发送
5.最后在handleMessage方法中处理消息

子线程创建Handler

在子线程中直接创建Handler会报错
错误日志为:

Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()"

代码中可以看到 Handler在创建时会判断当前线程的Looper是否为空

mLooper = Looper.myLooper();
if (mLooper == null) {
    throw new RuntimeException(
         "Can't create handler inside thread " + Thread.currentThread()
               + " that has not called Looper.prepare()");
}

为空的话则会报错。myLooper方法是获取当前线程的Looper,Looper存储早sThreadLocal

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

sThreadLocal是ThreadLocal<Looper>类型。ThreadLocal中的对象set/get方法获取到的数据都是当前线程的变量。
当myLooper为空时则说明当前线程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));
}

所以子线程中如果创建Handler则需要调用Looper.prepare()方法。而Looper.loop方法则是核心,消息的传递全依赖于此。

Handler原理

Handler的运行机制依赖于MessageQueue与Looper的支持。
我们从代码角度来看一下这三者之间的关系:
从入口Handler.sendMessage方法看起:

public final boolean sendMessage(@NonNull Message msg) {
    return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
}
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
}

sendMessage方法链看下来,最终调用了mQueueenqueueMessage方法,mQueue是MessageQueue类型,是Looper的成员变量
在Handler初始化方法赋值。下面我们看一下MessageQueue的enqueueMessage方法:

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        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;
            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 {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

从这边可以看到MessageQueue内部存储采用的是链表格式,本次方法的作用是将消息插入到链尾。整体逻辑是链表为空时,将msg使用mMessages保存下来,然后唤醒。当数据不为空时,遍历链表将数据插入到链尾,并唤醒。
综上所述Handler的sendMessage方法本质上就是一次插入方法,目的是将消息插入到MessageQueue链尾。
我们知道在子线程中发送消息之后则需要调用Looper.loop方法,否则消息不生效。下面我们来看一下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);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } catch (Exception exception) {
                if (observer != null) {
                    observer.dispatchingThrewException(token, msg, exception);
                }
                throw exception;
            } finally {
                ThreadLocalWorkSource.restore(origWorkSource);
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ...
        }
    }

上面是省略了的代码,可以看到loop是个死循环,唯一的退出操作是MessageQueue的next方法返回为空。当Looper调用quit或者quitSafely方法的时候,next会返回为空,此时Looper会跳出循环。next方法是阻塞操作,当没有消息时,next会一直阻塞在那里,导致loop方法也阻塞在那里。直到next方法返回了新消息,Looper就会处理新消息。当获取到信息时,会通过msg.target.dispatchMessage(msg)处理,target就是发送这条消息的Handler(Handler的enqueueMessage方法中将自身设置给msg(msg.target = this)),最终消息链回到了Handler的dispatchMessage方法中:

 public void dispatchMessage(@NonNull Message msg) {
         if (msg.callback != null) {
             handleCallback(msg);
         } else {
             if (mCallback != null) {
                 if (mCallback.handleMessage(msg)) {
                     return;
                 }
             }
             handleMessage(msg);
         }
     }

msg.callback是Runable类型,当我们调用Handler.post(runable)的时候,最终是将Runable设置给Message的callback变量。
dispatchMessage判断msg.callback是否为空,不为空调用handleCallback方法:

private static void handleCallback(Message message) {
        message.callback.run();
}

就是调用了Runable的run方法。而callback为空时则判断mCallback是否为空,mCallback是Callback类型

    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }

是Handler提供的一种非侵入式的回调,当你不想重写handler时可以设置Callback,在handleMessage处理消息并返回为true。
否则则调用自身handleMessage方法,这个方法子类重写后便可以处理消息。

到这里handler调用原理就走通了,那么有几个问题?

1.View.post Handler.post 和普通的发送有什么区别
2.主线程直接创建Handler为什么不报错
3.loop阻塞了为什么不会影响主线程。
View.post
public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
}

View.post方法判断有两条,一如果AttachInfo不为空则直接使用它的handler.post处理Runable。
如果为空的话,则使用HandlerActionQueue.post。HandlerActionQueue是一个队列,内部维护这一个数组。
post方法只是将Runable封装成HandlerAction放入到数组中

    public void post(Runnable action) {
        postDelayed(action, 0);
    }

    public void postDelayed(Runnable action, long delayMillis) {
        final HandlerAction handlerAction = new HandlerAction(action, delayMillis);

        synchronized (this) {
            if (mActions == null) {
                mActions = new HandlerAction[4];
            }
            mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
            mCount++;
        }
    }

在View的dispatchAttachedToWindow方法中会调用HandlerActionQueue的executeActions方法,遍历数组通过Handler.postDelayed处理信息

    public void executeActions(Handler handler) {
        synchronized (this) {
            final HandlerAction[] actions = mActions;
            for (int i = 0, count = mCount; i < count; i++) {
                final HandlerAction handlerAction = actions[i];
                handler.postDelayed(handlerAction.action, handlerAction.delay);
            }

            mActions = null;
            mCount = 0;
        }
    }

我们之前分析过Handler的dispatchMessage方法。其中针对Message的callback是否为空做了条件判定。callback是Runable类型。
Handler.post方法

    public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

    private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
    }

Handler.post方法其实也是调用了sendMessageDelayed来发送消息,区别在于在获取Message的时候将Runable设置给Message的callback属性,在最终分发的方法dispatchMessage中依据callback是否为空来判定是否Runable的run方法还是Handler自己的回调方法。
到这边我们可以解释了View.post和Handler.post本质上并没有不同,都是依赖于Handler发送消息机制,区别在于最终消息的回调方法不同。

在子线程中直接创建handler会报错,主线程直接创建Handler为什么不报错

Looper在主线程入口函数中调用了prepareMainLooper方法,该方法是是创建主线程的Looper,因此我们在主线程中创建handler时,Looper已经存在,所以不会报错。

Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

Android底层采用的是pipe/epoll机制,当主线程的MessageQueue没有消息时,便阻塞在loop的queue.next方法的nativePullOnce里,此时主线程会释放CPU资源进入休眠状态,直到下一条消息发送或者有事务到达时,通过向pipe管道写端写入数据来唤醒主线程工作。这边采用的epoll机制是一种IO多路复用的机制,可以同时监听多个描述符,当某个描述符就绪(读/写就绪)就立即通知相应程序进行读或者写操作,本质同步I/O,即读写是阻塞的。因此主线程大多数情况下处于休眠状态,所以不会大量消耗CPU资源导致卡死

epoll提供了三种方法
epoll_create(): 创建一个epoll实例并返回相应的文件描述符(epoll_create1() 扩展了epoll_create() 的功能)。
epoll_ctl(): 注册相关的文件描述符使用
epoll_wait(): 可以用于等待IO事件。如果当前没有可用的事件,这个函数会阻塞调用线程。
详情参考https://my.oschina.net/u/3863980/blog/1933086

Activity是如何在死循环中执行生命周期方法的?

通过Handler机制执行生命周期方法的。ActivityThread中有内部类H继承Handler。
Activity生命周期依靠looper.loop,当Handler接受到消息时,在其内部handleMessage方法中处理对应的生命周期方法。

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

推荐阅读更多精彩内容

  • 开发Android一般都用遇到使用handler的情况,现在因为rxjava的时候可能就减少了handler的使用...
    KIDNG_LGJ阅读 218评论 0 0
  • 要分析Handler的原理,首先需要了解Message和Looper,所以我们先来分析一下Message和Loop...
    王小二的王阅读 469评论 0 0
  • 简介# Handler 在 Android 开发中非常常见,它的常见用法相信只要稍微学过一些 Android 基础...
    王尼小老板阅读 853评论 2 17
  • 首先从Handler 的构造方法开始,Handler有几种构造方法,先从最开始的最普通的开始,Handelr ha...
    feary阅读 680评论 0 2
  • 前言 在Android开发的多线程应用场景中,Handler机制十分常用 今天,我将手把手带你深入分析Handle...
    BrotherChen阅读 468评论 0 0