android handler机制源码解析(二)

Android异步消息机制 Looper

如果不了解handler,请先看一下我的上一篇handler详解
android handler机制源码解析

先简单了解下looper的作用

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

从looper的构造函数可以看到:looper必须和messageQueue以及thread相关,之前也提到过每个looper有一个自己的messageQueue,每个thread最多只有一个looper。looper创建好之后,调用loop()开始循环,只要对应的msgQueue里面有msg了(handler发送过来的),就取出来处理,调用对应的handler去dispatchMessage

Looper.prepare()

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

从prepare源码中可以看到,会去new一个当前线程的looper放在ThreadLocal里面。ThreadLocal可以简单理解为一个map,可以通过thread找到looper;由于是threadLocal的put方法,prepare的结果只可能是增加一个或者更新一个,所以一个线程最多只持有一个looper;

  • 还记得之前说的,handler创建也需要对应的looper吗?因为handler创建的时候,如果默认什么参数都没有,会根据Thread.currentThread去threadLocal里面把对应的looper取出来。如果这时候,该线程事先没有looper,那么就会报错。
 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());
            }
        }

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

看源码的log: “Can’t create handler inside thread that has not called Looper.prepare()”,是不是这个异常很熟悉?
所以如果直接new handler()必须要先创建looper(Looper.prepare);

那么可能有人会提出疑问,平时我直接new一个handler(),然后运行也没见崩溃啊。这是因为啊,在ui线程创建的时候,就已经自己创建了looper,而且这个smainLooper是ui线程专有的(可能因为UI线程是唯一的,所以smainLooper也是唯一的吧)。自己new的一个线程构造handler之前一定记得Looper.prepare,然后调用Looper.loop()开始循环处理发送到messageQueue的消息。

  • 当然我们也可以给handler传一个looper,而不是让它默认去当前线程找looper。常用的就是把主线程的looper传进去吧。
new Handler(Looper.getMainLooper()).post(Runnable);

没错,这又是一种切到ui线程的方式,和runOnUiThread性质是一样的,走handler.post,最后都是把msg入队到UI线程的msgQueue(还记得起为什么runOnUiThread会切到ui线程吗,不懂的请看第一篇);

Looper.loop()

这个方法其实就是开始循环,一直监听;从执行这个函数的线程里面把对应的looper取出来,拿到对应的msgQueue。把里面的消息取出来,调用对应的handler去分发处理消息,后面的就是handler处理消息了。

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.isTagEnabled(traceTag)) {
                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();
        }
    }

所以在子线程里面要记得调用loop();不然就会出现msg发送倒是成功了,但是不会有人取出来对这个msg进行分发处理。
贴一个简单的demo吧:

//ui线程
 private TestThread testThread = new TestThread();

 Log.v("tag","ui主线程发送msg "+"当前线程:"+Thread.currentThread().getId());

  testThread.mhandler.sendEmptyMessage(0);

  //另起一个线程
 class  TestThread extends Thread{
        private Handler mhandler;
            @Override
            public void run() {
                    Looper.prepare();
                    mhandler = new Handler() {
                        @Override
                        public void handleMessage(Message msg) {
                            Log.v("tag", "子线程接收msg "+"当前线程: "+Thread.currentThread().getId());
                        }
                    };
                    Looper.loop();
        }
    }

运行结果:

运行结果
可以看到UI线程发的消息已经传到子线程处理了(主线程的threadId为1,唯一的主线程所以才配了个唯一的sMainLooper吧)
还有个问题:为什么looper知道这个msg该交给哪个handler处理呢?
其实很简单:handler发送消息入队的时候,就把自己的引用给msg了,msg有一个target属性(就是上面的msg.target.dispatchMessage(msg);)就是专门用来保存与handler的连接的。looper取出message,再找到msg对应的handler分发。

如果有讲的不清楚或者有误的地方,请指出。大家共同进步,谢谢

RNG凉了,贼难受 = =

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值