Handler源码学习笔记

Handler是我们在Android开发过程中经常会使用到或者与之打交道的一个类,比如我们会在子线程发起网络请求,然后到主线程进行UI的刷新。虽然现在有很多能够自由进行线程切换的相关库,比如RxJava,但了解Handler的原理还是很有必要的,也能够帮助我们更好的去使用相关库,其中的设计思想也特别重要。
1、Handler是如何创建的?
What?这也太简单了吧,看我们平常经常写的一段代码

	private Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    };

这不是就创建了一个Handler么?但是,我们所写的这段代码究竟干了什么事呢?我们看Handler的构造函数

	//当我们new Handler()的时候,调用的是这个无参的构造方法,其最终会调用下面一个构造方法
	public Handler() {
        this(null, false);
    }
    public Handler(@Nullable 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) {//注意这个异常,点1
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

以上代码就创建了一个Handler,其中需要注意的是点1,获取一个Looper对象,并且如果mLooper == null的时候会抛出异常。这就尴尬了啊,我们好像没有对Looper实例化啊,那这个Looper是在什么时候创建的呢?其实我们如果在主线程创建Handler的时候,其实得到的是主线程的Looper。如果我们将创建Handler的时候,如果我们在子线程创建Handler,如果没有调用Looper.prepare(),那么就会抛出上面点1的那个异常。所以如果我们是在子线程创建Handler需要这样创建。

//准备Looper
Looper.prepare();
//创建Handler
Handler handler = new Handler(){
   @Override
   public void handleMessage(@NonNull Message msg) {
       super.handleMessage(msg);
   }
};
//开启消息循环
Looper.loop();

通过以上我们就创建出了我们的Handler。那么问题点又来了,我们在主线程创建Handler的时候好像并没有调用Looper.prepare();Looper.loop();这2个方法啊,为什么没抛异常呢?其实答案就是并不是没有调用,而是不需要我们手动去调用,系统已经自动帮我们调用了这2个方法。在ActivityThread这个类的main方法,也是整个应用程序的主入口处。我们其实都知道java代码的运行是需要main函数作为主入口的,而我们自己写的时候也没有写这个方法,而能运行起来的关键也是系统帮我们做了这件事。代码如下:

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

       	............//此部分忽略很多代码

        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        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();
        }

        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");
    }

好了,我们的Handler已经被创建了,那么消息是怎么放入,又是怎么取出的呢?接下来我们先来看消息的放入。
一、消息如何放入
我们平常使用Handler发消息有2个系列,sendMessage和post系列,其不同点在于send是直接发送一个Message,回调在我们 new Handler重写handleMessage的地方。而post系列需要传入一个Runnable,会回调到此Runnable里进行消息处理。sendMeage有很多不同参数的形态,我们以sendMessageDelayed作为例子看下代码。

public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

为什么要看这个方法呢?其实看源码我们发现就算我们调用sendMessage其实它也是调用sendMessageDelayed这个方法。这段代码唯一需要注意的是SystemClock.uptimeMillis(),这个方法得到的是开机到现在所经历的时间毫秒数,然后加上我们所希望延迟的毫秒数,不就实现了一个延时执行的效果么。然后最终又调用了sendMessageAtTime这个方法,我们看下这个方法又做了什么。

	public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
		//mQueue是我们创建Handler的时候得到的
        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);
    }
    //上面的方法调用了此方法 传入queue message time
    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);
    }
    //这方法是MessageQueue里的哈
    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标记为可用状态
            msg.markInUse();
            //希望在何时执行
            msg.when = when;
            //mMessages可用理解为上一次入队的Message
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
            	//p == null MessageQueue的Message为空 说明存储Message的链表是空的,我们的Message应该放在第一个
            	//when == 0 希望立即执行 需要放在第一个
            	//when < p.when 希望执行的时间比mMessage的时间小,也是要放在第一个
                // New head, wake up the event queue if blocked.
                //将当前Message的next指向mMessage
                msg.next = p;
                //更新mMessage
                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();
                //if里条件不满足的时候,说明消息需要做插队处理
                Message prev;
                for (;;) {
                	//先将mMessage指向prev 注意此时的mMessage就是上次入队的Message并且一定不为空
                    prev = p;
                    //将p指向p的下一个Message
                    p = p.next;
                    //p==null 说明后面没有Message了,可用插入此位置
                    //when < p.when 说明希望执行的时间比p的时间小,就应该插入此位置
                    //当满足以上2个条件的任意一个,说明已经找到了要插入的位置,跳出循环
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                //将msg的next执向p
                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.enqueueMessage(Message msg, long when)之后,就完成了我们Message的入队操作。里面的核心步骤也做了相应的注释,有的步骤理解起来有点傲,需要耐心的去慢慢推导。
二、消息如何取出
消息的取出其实就是Looper.loop()开启之后,然后进入一个死循环,去尝试取出我们的Message。我们Looper.loop()的代码。

	public static void loop() {
		//得到Looper自己
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //得到queue
        final MessageQueue queue = me.mQueue;
        .................//此部分代码省略
        for (;;) {
        	//去queue里面获取Message  具体看后面的代码next()方法
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ............//此部分代码省略
            try {
            	//dispatchMessage 调用的是Handler的方法,在下面的代码讲解
                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);
                }
            }
           	..............//此部分代码省略
            msg.recycleUnchecked();
        }
    }
    //MessageQueue里面的方法
    Message next() {
        .........//此部分代码省略
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
			//这是本地方法,主要是做休眠唤醒,节约资源
            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;
                //msg不为空且msg.target为空 这个target其实就是Handler  说明msg不可用,要重新去拿
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    //开启循环去拿消息,直到拿到满足我们条件的消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                	//这里面是拿到了消息,做一些善后的事
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        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();
                        //最终会将我们的msg返回回去
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }
			...............//此部分代码省略
            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }
    //msg.target.dispatchMessage(msg); 调用的就是此方法
    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {//msg.callback其实是通过post系列进来的  callback其实是一个Runnable
            handleCallback(msg);
        } else {//这部分是send系列的回调处理
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //是不是特熟悉?不就是new Handler我们会重新的接收消息的方法么
            handleMessage(msg);
        }
    }

以上贴出的为关键部分的代码,通过以上的流程就可以成功的取到消息了。
以上我们明确了Handler的创建,消息如何入队、出队。那么Handler、Message、MessageQueue、Looper在其中他们各自扮演了什么角色?还有我们常说的Handler导致内存泄漏又是怎么一回事呢?我们先看下这个几个类各自的持有对象情况。
Handler:
Looper mLooper;//Looper对象
MessageQueue mQueue;//MessageQueue对象
Callback mCallback;//回调 在new Handler中可以传入
Message:
Handler target;//持有的Handler
Runnable callback;//post系列传进来的Runnable
Message next;//当前Message的下一个Message
MessageQueue:
Message mMessages;//上一次入队的Message
//MessageQueue最主要负责了入队、出队的方法,以及一些Native方法
Looper:
Looper sMainLooper;//主线程Looper
MessageQueue mQueue;//MessageQueue对象
Thread mThread;//当前的线程对象
ThreadLocal sThreadLocal;//可以理解为Map存储,线程数据隔离

Handler通过send或者post系列,发送Message,MessageQueue负责Message的入队、出队操作,Looper负责震整个消息调度,最核心的为loop()方法,不断的从里面取出消息,发送到负责处理消息的Handler。

关于会引起内存泄漏,我们看下对象的引用线:Looper->MessageQueue->Message->Handler(message.target)->Activity 如果Handler非静态类,那么就是这样一条引用线,也就是说如果不需要的Message不移除,那么所持有的Handler就不会移除,而Handler又持有了Activity的引用,直到消息被出队。所以我们需要在Activity的onDestory中调用handler.removeCallbacksAndMessages(null);将Message移除。

关于Message的创建,new Message()和Message.obtain() ,前一种是直接申请一个Message内存空间,而后一种会在废弃Message池中复用Message对象,而好处就是可以内存复用,减少gc次数。这种内存复用池的方法在很多框架设计中都会用到,属于内存优化的范畴。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值