Handler解析

作者: phoenixsky | 来源:发表于2016-05-18 18:05 被阅读264次

    什么是Handler?

    android处理用来更新UI的一套机制,也是消息处理的一套机制,可以用来发送消息,也可以用来处理消息.
    多个生产者,一个消费者的线程模型.

    为什么要用Handler

    异步耗时操作,使用handler更新UI

    怎么使用

    发送/移除消息

    Handler handler = new Handler(){
      @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //主线程的回调
            }
    }
    
    //第一种 
    Message message = new Message();
    //...arg1,arg2,what,obj相关赋值
    handler.sendMessage(message);
    
    //第二种 复用msg
    Message message = handler.obtainMessage();
    //...arg1,arg2,what,obj相关赋值
    message.sendToTarget();
    
    
    //第三种 直接发送
    handler.sendEmptyMessage();
    
    //第四种 延迟发送
    handler.sendMessageDelayed();
    
    //第五种 指定时间
    handler.sendMessageAtTime();
    
    //第六种 插入到队列前侧
    handler.sendMessageAtFrontOfQueue();
    
    //以上msg的发送方式,都会进入handler的handleMsg()方法里
    
    
    //第七种 该方法将直接调用Message内部的Runnable(具体见handler的dispathMessage()下文在looper部分时有讲到)
    Message.obtain(handler, new Runnable() {
                        @Override
                        public void run() {
                        }
                    }).sendToTarget();
    
    
    //移除消息
    handler.removeCallBacks(runnable);
    

    处理消息

        Handler handler = new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                //截获msg消息 返回值true代表消息被截获,不再向下执行
                return false;
            }
        }) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
    

    为什么要设计只能通过Handler来更新UI

    1. 更新界面错乱
      多个线程更新UI,并且不加锁
    2. 性能下降
      加锁后性能就会下降

    所以通过主线程消息队列的方式来更新UI(避免多线程并发)

    Handler的原理

    Looper

    1. 内部包含了一个消息队列MessageQueue
    2. Looper.loop()方法,不断从MQ中取消息,有就将message交给target(也就是handler),没有就阻塞.

    MessageQueue

    1. 消息队列,消息的容器

    Handler

    内部与Looper关联,在handler内部可以找到looper,找到looper就是找到了MQ,

    Handler负责发送消息,Looper负责接收Handler发送的消息,并把消息回传给Handler自己.MQ就是一容器

    源码相关


    Looper

    1. ActivityThread.main()
    2. Looper.PrepareMainLooper();
    3. Looper.prepare(quitAllowed);
    4. if(ThreadLocal.get() == null){ThreadLocal.set(new Looper())};
    5. new MessageQueue();

    从ActivityThread的main()方法进入
    然后调用Prepare方法,
    new出Lopper对象,同时在looper的构造函数里,new出MQ对象
    然后将looper存入到ThreadLocal中


    ThreadLocal (线程本地变量)

    当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

    简单理解为这一个对象可以为每个线程存储不同的数据

    关于ThreadLocal不错的博文
    1. 理解ThreadLocal
    2. 彻底理解ThreadLocal

    Handler与Looper的关联

    1. Handler handler = new Handler();
    2. handler.mLooper = Looper.myLooper(); //return sThreadLocal.get()
    3. handler.mQueue = mLooper.mQueue;

    在Handler构造函数中,将当前线程存储的looper,mQueue对象.给到handler


    Looper.loop()

    用来开启消息循环.注意 该方法之后的代码都将不会执行.

    为了方便观察,移除了一些不必要代码,并将for循环,改成了while(true)

    public void loop(){
      while(true){
        // ...
        Message msg = mQueue.next();
        if(msg == null){
          return;
        }
        //这里的target就是handler自身
        msg.target.dispatchMessage();
        // ...
      }
    }
    // msg.target就是handler
    // 所以最终调用的handler的dispathMessage()
    
    public void dispatchMessage(Message msg) {
          if (msg.callback != null) {
          //此处对应上文handler第七种发送消息的处理
              handleCallback(msg);
          } else {
              if (mCallback != null) {
                  if (mCallback.handleMessage(msg)) {
                      return;
                  }
              }
              handleMessage(msg);
          }
      }
    

    sendMessage(Message message)

    将消息加入到MessageQueue中

    1. handler.sendMessage(msg); 发送消息
    2. msg.target = handler; 绑定target
    3. queue.enqueueMessage(message, uptimeMills); 将消息加入MessageQueue队列

    总结

    App在启动的时候,将主线程的Lopper和MessageQueue创建完毕.并将looper对象存至threadLocal对象,调用looper.loop()方法, 一直循环looper对象中的messageQueue.
    我们在编码时,在主线程实例一个handler对象,就会通过threadLocal对象将主线程的looper对象取出,并将looper和mq对象与handler关联.这样在handler.sendMessage(msg)后,就能通过looper遍历出的msg,取出handler,来调用dispatchMessage方法


    相关使用方式总结

    子线程中Handler

    Handler handler;
    new Thread() {
                @Override
                public void run() {
                    Looper.prepare();//将该线程的looper对象存入threadLocal
                    handler = new Handler() {
                        @Override
                        public void handleMessage(Message msg) {
                            Log.i(TAG, "handleMessage:  " + Thread.currentThread());
                        }
                    };
                    Looper.loop();
                }
            }.start();
    //休眠500毫秒,待子线程handler创建完成
    Thread.sleep(500); //可以通过HandlerThread来处理多线程并发时,对象为空的问题
    //主线程通知子线程处理消息
    handler.sendEmptyMessage(0);
    

    子线程中直接调用主线程的handler

    new Thread() {
                @Override
                public void run() {
                  // do something
                    Message.obtain(new Handler(getMainLooper()), new Runnable() {
                        @Override
                        public void run() {
                            Log.i(TAG, "Message.obtain " + Thread.currentThread());
                        }
                    }).sendToTarget();
            }.start();
    

    相关文章

      网友评论

        本文标题:Handler解析

        本文链接:https://www.haomeiwen.com/subject/pwpwrttx.html