Handler详解
博客专区 > libvirus 的博客 > 博客详情
Handler详解
libvirus 发表于1年前
Handler详解
  • 发表于 1年前
  • 阅读 13
  • 收藏 0
  • 点赞 2
  • 评论 0
摘要: 因为Android里的控件都是线程不安全的,更新UI的代码必须在主线程所有Google给我们提供了一个线程通信的类。 而且Android的各种事件,Activity,Service组件的方法回调,也是基于Hander的。 这就是Handler。其实他们主要是由三个主要的类构成 Looper , Handler , MessageQueue。

Handler详解

##一,概述 因为Android里的控件都是线程不安全的,更新UI的代码必须在主线程所有Google给我们提供了一个线程通信的类。 而且Android的各种事件,Activity,Service组件的方法回调,也是基于Hander的。 这就是Handler。其实他们主要是由三个主要的类构成 Looper , Handler , MessageQueue。

##二,Looper 我相信写过从主线程发送消息通知子线程的人都应该刻这一段代码

        Looper.prepare();
        Handler handler=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        Looper.loop();

为什么在主线程写Handler不用Looper.prepare(); Looper.loop();呢,那是因为主线程已经为我们做了这些工作。 我们可以看看这两行代码做了什么事。如下,Looper.prepare()主要是初始化工作,创建一个Looper对象放入ThreadLocal当中。然后新建一个MessageQueue对象。 我们通过Handler 发送的消息就放在这个队列当中。

public static void prepare() {
        prepare(true);
    }

    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));
    }
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

在看看Looper.loop()又做了点什么事情

    /**
     * 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();
        final MessageQueue queue = me.mQueue;

        for (;;) {
            Message msg = queue.next(); // might block
            msg.target.dispatchMessage(msg);
            msg.recycleUnchecked();
        }
    }

这个方法很简单只是把死循环队列当中的消息,有消息就msg.target.dispatchMessage去执行,dispatchMessage这个方法是不是很熟悉呢,其它msg.target 对象就是Handler对象,至于怎么放到Message当中去的,后边在讲。有细心的同学可能注意到这是一个死循环,而且队列有可能会阻塞,前面有讲到主线和里替我们 初始化了Looper。那么主线程为又是怎么执行代码的呢。当然原因很简单方法回调和事件都是通过主线程里的Handler发送到队列里,然后执行的。比如你手机卡的 时候你点返回,没有反应,你连点几次的话,又退出程序了,因为事件都在队列里,执行完一个在执行别一个。 ##三,Handler 上面说到Looper.loop()从队列里取出来的Message给了msg.target,我说他是一个Handler对象,我们看看他是怎么放入消息中的。

    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

所有发送消息的方法最后都是调用的这个方法。我们看到msg.target=this所有消息在放入消息队列之前都会持有发送消息的Handler对象。这就是哪个Handler发送的消息, 哪个Handler负责处理。 那么,Handler是怎么和Looper关联起来呢,Handler怎么知道消息要放入哪个队列的呢。原来在

   public Handler(Callback callback, boolean async) {
        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;
    }

我们看到从这个Looper.myLooper()方法里得到的Looper对象。

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

现在知道Looper对象是怎么来的了吧。如果在非主线程里new Handler那就必须要在这个线程里初始化一个Looper对象,用ThreadLocal保存起来。new Handler的时候 要查检在本线程里有没有这个对象,如果没有会报错。

##三,总结 对于Rxjava,RxAndroid 这类不用写Handler的类库,内部应该也是需要通过Handler post代码的。所以这种线程间通信的方法一定要明白。 当然这还些知道还很浅显。最核心的是消息队列的实现,消息队列是由Linux里的管道实现的。希望以后搞明白了那一块的代码,在来更新。

共有 人打赏支持
粉丝 0
博文 2
码字总数 1315
×
libvirus
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: