粗读 Android Handler 源码

粗读 Android Handler 源码

Handler源码和实现机制主要包含了几个关键的类Handler、Message、ThreadLocal、Looper、MessageQueue、ActivityThread。
当我一次无意中在子线程中尝试创建一个Handler实例的时候,果断程序就奔溃报错了。

“Can’t create handler inside thread that has not called Looper.prepare()”

意思大概是说不能在Looper.prepare()方法没执行前创建Handler对象。当时也没在意就把它放到了外面初始化了。后面看源码的时候自己就思考这个问题为啥在外面可以创建呢?原来报错的原因是因为因为mLooper对象为空值


        //获得一个Looper对象实例
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            //说明Looper对象初始化失败了(没有初始化)
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }

````
那按这样推倒意思就是说Looper.prepare()方法的必须在之前执行过啊。看下源码





<div class="se-preview-section-delimiter"></div>

``` java

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


      /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

果然如我们所料啊。原来Looper.prepare()方法的作用是做了初始化的工作。那我们回到前面说的,那我在在线程中创建Handler实例之前执行下这个方法是不是就可以呢?答案是肯定的。那有人肯定会问为啥直接在主线程中创建Handler实例就不会报异常呢,按这个来推倒的话那肯定是在主线程某个地方做了这件事啊。这时候我们的ActivityThread就登场了,咋们直接定位到他的main()函数。查看里面的代码

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

我们主要关心的不多只有这几句代码这里Looper.prepareMainLooper()其实最终也是调用了啊Looper.prepare(),除此之外我们还有意外收获那就是另一个很重要的方法 Looper.loop()调用了这个方法。
这方法调用的时机一般是在Handler初始化完成之后,那我们就可以判断出Activity的一些创建 绘制什么的肯定也是在Looper.prepareMainLooper()和Looper.loop()进行的至于为什么这里就不细说了。出门百度 O(∩_∩)O哈哈~
好那我们去看下这个核心方法吧


/**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the 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;

        // 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 (;;) {
        //获取一个消息对象
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            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();
        }
    }

这里用了一个for(;;)循环对消息队列进行无限轮询,我们只要关心主要的代码就可以了
msg.target.dispatchMessage(msg),这个方法就是分发我们的消息的进去我们可以看到如下


    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

   /**
     * 当以Post形式发送消息的时候回调其
     * run方法
     * @param message
     */
    private static void handleCallback(Message message) {
        message.callback.run();
    }

至此我们熟悉的终于出现了,这里主要体现了一个优先级Handler消息接收也有这两种方法。接口优先然后才是重写的方法。如果接口为空则调用handleMessage(msg);最终就到了我们这里了

    private  Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 1:
                String s= (String) msg.obj;
                tv1.setText(s);
                break;
            }

        }
    };

* 所以我们要在子线程中模仿上面的步骤自己实现一个Handler除了在创建Hnadler实例之前需要调用Looper.prepare()方法还需在实例创建完成后执行Looper.poop()方法启动消息队列轮询 *

让我们回到开始,我们使用Handler发送消息一般有两种方式分别是是handler.sendXXX()和handler.postXXX(),但是不管哪一种最终都是调用了sendMessageAtTime(Message msg,long uptimeMillis)方法,然后在调用MessageQueue的enqueueMessage()方法,作用是往队列里面插入一条Message,然后再loop中通过next方法不断地取出Message进行处理。

盗用网上一张图描述这几者之间的关系网(希望作者不要跟我一般计较):

mark

以上就是我看源码的一些过程,期间也有参阅一些资料。感谢那些乐于分享的前辈!!
可能有点乱,全当记录一下。供以后翻阅吧,如有错…请指出。轻喷哈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值