Android Hanlder的理解

Handler 中有四个关键类

  1. Message
  2. Handler
  3. MessageQueue
  4. Looper

介绍一下这四个类的作用

Message

Meaage实现了Parcelable接口,从这里就可以看出,它是一个可序列化的类
在发送消息前,需要把消息事件包装为Message,这里有几个注意事项:
1、这里推荐使用Message.obtain() 来创建Message对象,
2、在每次发送消息前创建Message对象,避免Message对象存在的时间太长,而且在发送Message消息前要使用handler.hasMessages()进行判断,是否已经有此消息,如果有,根据需要进行移除获取return操作。这里的操作都是为了避免出现RuntimeException:This Message is aleady in use的异常。

Handler

用于接收消息,最终处理消息

Handler在构造过程中会确立以下几件事:

  1. 获取当前Handler实例所在线程的Looper对象,mLooper = Looper.myLoopper();
  2. 如果Looper不为空,则获取Looper的消息队列,赋值给Handler的成员变量mQueue:mQueue = mLooper.mQueue
  3. 可以设置CallBack来处理消息回调:mCallback = callback

Handler创建过程可以看出其两个特性:

  1. Handler只能绑定一个线程的Looper;
  2. Handler的消息是发送给Looper的MessageQueue, 需要等待处理;

所以,如果在子线程中声明了一个Handler,是不能直接更新UI的,需要在Handler的构造方法中传入主线程的Looper。
并且要注意,子线程没有默认的Looper,在子线程创建Handler之前,必须先开启子线程的Looper,否则会爆出异常。

  • 发送消息关键方法:
    Handler sendMessage(),最终调用到sendMessageAtTime,调用enqueueMessage,将消息传入MessageQueue,
  • 接收消息关键方法
    Handler dispatchMessage(),如果Message的Callback不为空,则回调到Message的Callback中,如果Message中的Callback为空,则判断Handler中的Callback是否为空,如果不为空,则回调到Handler中的Callback来处理,如果都没有传入Callback,则由handleMessage 处理
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

MessageQueue

主要功能有两个,一个插入消息,关键方法enqueueMessage,一个取出消息next,通过单向链表的数据结构来存储消息。下面结合源码进行理解。

boolean enqueueMessage(Message msg, long when) {
		// 如果msg没有target则抛出异常,target就是Handler
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        ...
		//插入消息时,因为是队列,有先后之分,需要同步
        synchronized (this) {
            ...
            msg.markInUse();
            msg.when = when; // when是系统开机时间+延迟时间,得出的消息执行时间
            Message p = mMessages; // 头部消息
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
            	// p == null 代表当前消息队列中没有消息
            	// when == 0 代表系统刚开机,基本不可能出现
            	// when < p.when 代表此消息的执行时间早于此队列中所有的消息
				// 将此消息放置在消息队列的头部
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {            	
                // 插入队列中间。通常我们不必唤醒事件队列,
                // 除非队列的头部有障碍物,
                // 并且消息是队列中最早的异步消息。
                // (p.target == null 则代表他是一个同步屏障,且当前消息为异步消息,异步消息优先)
                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) {
            	// 这个方法可以让nativePollOnce立即返回
                nativeWake(mPtr);
            }
        }
        return true;
    }
Message next() {
        // 如果消息循环已退出并已被释放,请返回此处。如果应用程序在退出后尝试重新启动循环器,则可能会发生这种情况,这是不受支持的。
        // native中MessageQueue的地址值
        final long ptr = mPtr;
        if (ptr == 0) {
            // 如果为0 则说明应用已退出
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

			// 本方法就是执行native消息层
			// nextPollTimeoutMillis是超时等待时间,如果为-1 则表示无限等待,这个方法会阻塞循环
			// 如果值为0 ,则无需等待立即返回
			// 通过Linux的epoll机制进行阻塞
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                	// msg.target == null 代表其是屏障消息,则跳过同步消息,寻找下一条异步消息。
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    if (now < msg.when) {
                        // 比较消息的时间,是不是比当前的时间要大,则需要设置一个延迟时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 获取一条消息
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // 没有更多消息时也会进行等待
                    nextPollTimeoutMillis = -1;
                }
                // 下面是进行一些idleHandler的处理
                // 此处暂时不展开了
                ...
            }
			...
        }
    }

Looper

Looper是自从启动后就开始无限循环,不断的从消息队列中获取消息,然后分发处理事件,APP启动时,在ActivityThread的main()方法中已经启动了Looper循环,关键方法 loop();一下是源码中的loop方法:

/**
119     * Run the message queue in this thread. Be sure to call
120     * {@link #quit()} to end the loop.
121     */
122    public static void loop() {
123        final Looper me = myLooper();
124        if (me == null) {
125            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
126        }
127        final MessageQueue queue = me.mQueue;
128
129        // Make sure the identity of this thread is that of the local process,
130        // and keep track of what that identity token actually is.
131        Binder.clearCallingIdentity();
132        final long ident = Binder.clearCallingIdentity();
133
134        for (;;) {
135            Message msg = queue.next(); // might block
136            if (msg == null) {
137                // No message indicates that the message queue is quitting.
138                return;
139            }
140
141            // This must be in a local variable, in case a UI event sets the logger
142            Printer logging = me.mLogging;
143            if (logging != null) {
144                logging.println(">>>>> Dispatching to " + msg.target + " " +
145                        msg.callback + ": " + msg.what);
146            }
147
148            msg.target.dispatchMessage(msg);
149
150            if (logging != null) {
151                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
152            }
153
154            // Make sure that during the course of dispatching the
155            // identity of the thread wasn't corrupted.
156            final long newIdent = Binder.clearCallingIdentity();
157            if (ident != newIdent) {
158                Log.wtf(TAG, "Thread identity changed from 0x"
159                        + Long.toHexString(ident) + " to 0x"
160                        + Long.toHexString(newIdent) + " while dispatching to "
161                        + msg.target.getClass().getName() + " "
162                        + msg.callback + " what=" + msg.what);
163            }
164
165            msg.recycleUnchecked();
166        }
167    }

重点流程
1、获取Looper对象
2、获取MessageQueue对象
3、死循环遍历
4、通过queue.next获取一条Message
5、通过msg.target.dispatchMessage(msg) 分发Message
6、通过msg.recycleUnchecked() 回收Message到消息池中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值