美文网首页
Handler异步通信机制解析

Handler异步通信机制解析

作者: 小浩_w | 来源:发表于2018-10-09 17:37 被阅读0次

    定义

    Android提供的一套消息传递机制


    作用

    在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理


    image.png

    用于实现子线程对UI线程的更新,实现异步消息的处理:

    • 在新启动的线程中发送消息
    • 在主线程中获取并处理信息

    为什么要用 Handler消息传递机制?

    • 多个线程并发更新UI的同时 保证线程安全

    相关概念

    image.png
    • 主线程(UI线程)

    定义:当程序第一次启动时,Android会同时启动一条主线程(Main Thread)作用:主线程主要负责处理与UI相关的事件,所以主线程又叫UI线程
    子线程则负责一些比较耗时的操作(联网、取数据、SD卡数据加载等操作),而主线程和子线程之间的通信,就是要靠Handler了。

    • Message

    定义:消息,理解为线程间通讯的数据单元(Handler接受和处理的消息对象。)
    例如后台线程在处理数据完毕后需要更新UI,则可发送一条包含更新信息的Message给UI线程

    • Message Queue

    定义:消息队列作用:用来存放通过Handler发过来的消息,按照先进先出执行
    Handler
    定义:Handler是Message的主要处理者作用:负责将Message添加到消息队列&处理Looper分派过来的Message

    • Looper

    定义:循环器,扮演Message Queue和Handler之间桥梁的角色
    作用:主要负责消息循环:循环取出Message Queue的Message;消息派发:将取出的Message交付给相应的Handler

    每个线程中只能拥有一个Looper,但是一个Looper可以和多个线程的Handler绑定起来,也就是说很多个线程可以往一个Looper所持有的MessageQueue中发送消息。这就给我们提供了线程之间通信的可能。Handler在创建的时候可以显示指定Looper,这样在Handler在调用sendMessage()投递消息的时候会将消息添加到指定的Looper里面的MessageQueue。如果不指定Looper,Handler默认绑定的是创建它的线程的Looper。

    Handler

    提供sendMessage方法,将消息放置到队列中提供handleMessage方法,定义个各种消息的处理方式;

    Handler的使用方式 因发送消息到消息队列的方式不同而不同
    共分为2种:使用Handler.sendMessage()、使用Handler.post()

    Looper

    Looper.prepare():实例化Looper对象;为当前线程生成一个消息队列;Looper.loop() :循环从消息队列中获取消息,交给Handler处理;此时线程处于无限循环中,不停的从MessageQueue中获取Message 消息 ;如果没有消息就阻塞

    MessageQueue

    提供enqueueMessage 方法,将消息根据时间放置到队列中;提供next方法,从队列中获取消息,没有消息的时候阻塞;

    Handler、Looper、MessageQueue关系类图


    image.png

    工作流程

    第一部分:Looper

    Looper主要负责:

    • 自身的创建&创建Message Queue
    • 消息循环(消息取出、派发)
    public static final void prepare() {
    //判断sThreadLocal是否为null,否则抛出异常
    //即Looper.prepare()方法不能被调用两次
    //也就是说,一个线程中只能对应一个Looper实例
         if (sThreadLocal.get() != null) {
             throw new RuntimeException("Only one Looper may be created per thread");
         }
    //sThreadLocal是一个ThreadLocal对象,用于在一个线程中存储变量
    //实例化Looper对象并存放在ThreadLocal
    //这说明Looper是存放在Thread线程里的
         sThreadLocal.set(new Looper(true));
    }
    //再来看下Looper的构造方法
    private Looper(boolean quitAllowed) {
    //创建了一个MessageQueue(消息队列)
    //这说明,当创建一个Looper实例时,会自动创建一个与之配对的MessageQueue(消息队列)
         mQueue = new MessageQueue(quitAllowed);
         mRun = true;
         mThread = Thread.currentThread();
    }
    
    

    消息循环:loop()方法

    public static void loop() {
            //myLooper()方法作用是返回sThreadLocal存储的Looper实例,如果me为null,loop()则抛出异常
            //也就是说loop方法的执行必须在prepare方法之后运行
            //也就是说,消息循环必须要先在线程当中创建Looper实例
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            //获取looper实例中的mQueue(消息队列)
            final MessageQueue queue = me.mQueue;
     
     
            Binder.clearCallingIdentity();
            final long ident = Binder.clearCallingIdentity();
            //进入消息循环
            for (;;) {
                //next()方法用于取出消息队列里的消息
                //如果取出的消息为空,则线程阻塞
                Message msg = queue.next(); // might block
                if (msg == null) {
     
                    return;
                }
     
     
                Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
     
              //消息派发:把消息派发给msg的target属性,然后用dispatchMessage方法去处理
              //Msg的target其实就是handler对象,下面会继续分析
                msg.target.dispatchMessage(msg);
     
                if (logging != null) {
                    logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
                }
     
                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.recycle();
            }
    }
    
    

    实例化本身、与当前线程绑定、创建与之相应的MessageQueue:prepare()方法

    一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue

    消息循环(消息取出、消息派发):loop()方法
    不断从MessageQueue中去取消息,派发给消息的target属性的Handler,然后调用相应Handler的dispatchMessage()方法进行消息处理。

    Handler

    1.在子线程发送消息给MessageQueue
    2.处理Looper派发过来的消息

    使用Handler之前,会初始化一个Handler实例
    Handler是需要和线程绑定在一起的,在初始化Handler的时候一般通过指定Looper对象从而绑定相应线程,即给Handler指定Looper对象=绑定到了Looper对象所在的线程中,Handler的消息处理回调会在那个线程中执行。一般有两种方法创建:

    1.通过Loop.myLooper()得到当前线程的Looper对象/通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。

    2.不指定Looper对象,那么这个Handler绑定到了创建这个线程的线程上,消息处理回调也就在创建线程中执行.

    Handler构造方法

    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());
                }
            }
    //通过Looper.myLooper()获取了当前线程保存的Looper实例,如果线程没有Looper实例那么会抛出异常
    //这说明在一个没有创建Looper的线程中是无法创建一个Handler对象的
    //所以说我们在子线程中创建一个Handler时首先需要创建Looper,并且开启消息循环才能够使用这个Handler。
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
    //获取了这个Looper实例中保存的MessageQueue(消息队列)
    //这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了
     
            mQueue = mLooper.mQueue;
            mCallback = callback;
            mAsynchronous = async;
        }
    
    

    Post的发送方法:sendMessage()

      showhandler.post(new Runnable() {
                    @Override
                    public void run() {
                        String line = "\n";
                        StringBuffer text = new StringBuffer(show.getText());
                                text.append(line).append("angelababy:Yes,I do");
                                show.setText(text);
                    }
    
    

    相比send方法,post方法最大的不同在于,更新UI操作可直接在重写的run方法定义。
    其实Runnable并没有创建什么线程,而是发送了一条消息,下面看源码:

     public final boolean post(Runnable r)
        {
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
     
      private static Message getPostMessage(Runnable r) {
    //创建了一个Message对象
    //创建Message对象可以new,也可以使用Message.obtain()方法;
    //但是更建议使用obtain方法,因为Message内部维护了一个Message池用于Message的复用,避免使用new 重新分配内存。
            Message m = Message.obtain();
    //将我们创建的Runable对象作为callback属性,赋值给了此message.
            m.callback = r;
    //创建了一个Message对象
            return m;
        }
     
     public final boolean sendMessageDelayed(Message msg, long delayMillis)
        {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }
     
     public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            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);
        }
    
    

    本文参考了以下博客:
    https://blog.csdn.net/qq_36388797/article/details/72622155
    https://blog.csdn.net/carson_ho/article/details/51290360

    相关文章

      网友评论

          本文标题:Handler异步通信机制解析

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