Android Handler机制详解原理

Looper是整个跨线程通信的管理者

    // 内部持有的变量如下:
    ThreadLocal<Looper>
    MainLooper
    Observer
    MessageQueue
    Thread

1.首先先回忆一下Handler怎么用

Android线程通信分为以下两种情况

  • 1.子线程发消息给UI线程
  • 2.UI线程发消息给子线程
  • 3.子线程发消息给另个子线程

1.子线程发消息给UI线程

class FragmentContentActivity : AppCompatActivity() {
    val FLAG = 1
    lateinit var handler: Handler
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        handler = object : Handler(Looper.getMainLooper()) {
            override fun handleMessage(msg: Message) {
                when (msg.what) {
                    FLAG -> {
                        findViewById<TextView>(R.id.text).text = msg.data["Text"].toString()
                    }
                }
            }
        }
        thread {
            Thread.sleep(2000L)
            handler.sendMessage(Message.obtain().apply {
                what = FLAG
                data = Bundle().apply {
                    this.putString("Text", "ThreadMessage")
                }
            })
        }
    }
}

2.UI线程/子线程发消息给子线程

class FragmentContentActivity : AppCompatActivity() {
    val THREAD_FLAG =2
    lateinit var threadHandler: Handler
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        thread {
            Looper.prepare()
            threadHandler = object :Handler(Looper.myLooper()!!){
                override fun handleMessage(msg: Message) {
                    when(msg.what){
                        THREAD_FLAG -> {
                            Toast.makeText(
                                this@FragmentContentActivity,
                                "${msg.data["Text"]}",
                                Toast.LENGTH_SHORT
                            ).show()
                        }
                    }
                }
            }
            Looper.loop()
        }
    }

    override fun onResume() {
        super.onResume()
        findViewById<TextView>(R.id.text).postDelayed({
           threadHandler.sendMessage(Message.obtain().apply {
               what = THREAD_FLAG
               data = Bundle().apply {
                   putString("Text","UI Message")
               }
           })
        },2000L)
    }
}

**在子线程的使用中,我们发现必须要进行Looper.prepare()和Looper.loop()前后这两个操作,因此,带着这个疑问来看一下Looper的逻辑
**

// 在调用prepare()之后一定要调用loop(),最后结束消息循环的时候调用quit()
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();
}

prepare()就是将初始化一个Looper对象放入到ThreadLocal中,初始化Looper,同时mQueue

    public static void loop(){

    Binder.clearCallingIdentity()
for (;;) {
    Message msg = queue.next(); // might block

    long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
    try {
        // 其实 loop()只做了这一个调用,其他的都是监控当前消息循环时间是否超时,应该和ANR有关
        msg.target.dispatchMessage(msg);
        if (observer != null) {
            observer.messageDispatched(token, msg);
        }
        dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
    } catch (Exception exception) {
        if (observer != null) {
            observer.dispatchingThrewException(token, msg, exception);
        }
        throw exception;
    } finally {
        ThreadLocalWorkSource.restore(origWorkSource);
        if (traceTag != 0) {
            Trace.traceEnd(traceTag);
        }
    }
    if (logSlowDelivery) {
        if (slowDeliveryDetected) {
            if ((dispatchStart - msg.when) <= 10) {
                Slog.w(TAG, "Drained");
                slowDeliveryDetected = false;
            }
        } else {
            if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                    msg)) {
                // Once we write a slow delivery log, suppress until the queue drains.
                slowDeliveryDetected = true;
            }
        }
    }
    if (logSlowDispatch) {
        showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
    }
    //消息实体回收
    msg.recycleUnchecked();

可以看到Looper.loop其实只是在for循环中,获取mQueue的下一个msg节点,然后调用msg.target.dispatchMessage(msg)。乍看只是msg对象内部的操作。

因为loop()其实逻辑上算死循环,这意味着,当前线程的自发的顺序执行命令到此结束了,只能通过其他线程触发handler机制,来被动的在当前线程执行命令,当前线程完全变成了一个响应线程

Looper类只是初始化并开启线程死循环的一个开关,具体工作在MessageQueue中进行

MessageQueue 消息队列

队列内消息的添加不是直接调用MessageQueue,而是由与Looper相关联的Handler调用

MessageQueue的内部持有的变量如下: ArrayList mMessages SparseArray IdleHandler[] mBlocked

MessageQueue类的功能主要有:元素插入队列,获取队列的头部元素,查找队列中元素,综述就是对队列的增删改查,其中 mMessage就是这个队列的入口也是这个队列的头结点

  boolean enqueueMessage(Message msg,long when) //msg 元素插入队列
  boolean hasMessages(Handler h,int what,Object object) //查找handler下的msg.what/object相同的Msg
  boolean hasEqualMessages(Handler h,int what,Object obj)//查找 msg.object.equal(obj)的msg
  removeMessages(Handler h,int what,Object obj)/(Handler h,Runnable r,Object obj)
  removeEqualMessages(...) //删除与参数msg.object相同或equal的msg

  Message next() //获取队列中的头部元素

可以看出,这些方法内部都调用了 synchronized(this),队列的操作都是线程同步的

Message next() ->
...

// linux机制下的总线进入轮询,线程相当于挂起状态,nextPollTimeOut是挂起多长时间
nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {

   final long now = SystemClock.uptimeMillis();
   Message prevMsg = null;
   Message msg = mMessages;
   //先判断msg.target是否为null,表示当前消息是不是异步消息
   if (msg != null && msg.target == null) {
   // Stalled by a barrier.  Find the next asynchronous message in the queue.
   //同步屏障:取出当前队列中的异步消息
   do {
       prevMsg = msg;
       msg = msg.next;
       } while (msg != null && !msg.isAsynchronous());
   }
   if (msg != null) {
       if (now < msg.when) {
           // Next message is not ready.  Set a timeout to wake up when it is ready.
           //重新计算线程进入挂起状态的时间
           nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
       } else {
           // Got a message.
               mBlocked = false;
               if (prevMsg != null) {
               prevMsg.next = msg.next;
           } else {
               mMessages = msg.next;
           }
           msg.next = null;
           if (DEBUG) Log.v(TAG, "Returning message: " + msg);
           msg.markInUse();
           return msg;
       }
   } else {
   // No more messages.
       nextPollTimeoutMillis = -1;
}
...

可以看出next()内部主要有两种获取msg的逻辑

1.当前消息都是普通消息,按照msg.when的大小排序,每一次循环执行,通过检测when是否大于now来决定是否获取msg,或是挂起当前线程。
2.当前消息中有异步消息,优先获取msg.isAsynchronous()==true的,或者按照此异步消息的等待时间,来重新设置挂起线程的时间,从而达到精准的获取异步消息。

通俗的来讲就是说,当前所有普通消息按照预约的执行时间的先后来排队,这样可基本上既可以达到按照预约时间执行消息,也可以最大效率的在一定时间段内执行最多的消息,但是这忽略了每个消息的执行消耗的时间,比如A消息是队列内的No.1,A消息预约执行时间是1s之后,整个队列是等待状态的,这个时候来了B消息,B消息预约的时间是0.999s之后,按照预约时间的排队机制,B消息要插队到A消息之前,B成了这个队列的No.1,A成了No.2,整个队列的等待时间还是1s(因为之前设置了等待时间,所以不用唤醒),但是B消息的执行过程长达0.5s,已经超过了之后的很多消息的预约执行时间点了,这样就不能保证某些重要的消息按时执行。

于是就有了异步消息同步屏障的机制,这相当于普通消息排队时来了一个VIP消息,先按照预约时间找到自己的位置,然后大喝一声:“都把脚给我挪开,我的前面不允许有人”,这个时候排在他之前的普通消息就只能全部挪到队列的一边,然后队列重新按照这位VIP消息,设置等待时间,期间新来的普通消息也插到队边等待,保证精准按时执行VIP消息。等VIP消息执行完,之后再把之前等待普通消息的队列合并执行。当然之前等待的消息全耽误了,但毕竟是普通消息不重要。

 // 同步屏障的方法,此方法只在 ViewRootImpl类中调用
private int postSyncBarrier(long when) {
    // Enqueue a new sync barrier token.
    // We don't need to wake the queue because the purpose of a barrier is to stall it.
    synchronized (this) {
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;
        //没有设置target
        Message prev = null;
        Message p = mMessages;
        if (when != 0) {
            while (p != null && p.when <= when) {
                prev = p;
                p = p.next;
            }
        }
        if (prev != null) { // invariant: p == prev.next
            msg.next = p;
            prev.next = msg;
        } else {
            msg.next = p;
        //mMessages变为同步屏障消息,next()下一次循环,首先获取到的是同步屏障
            mMessages = msg;
        }
        return token;
    }
    // ViewRootImpl
 void scheduleTraversals() {
        if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }

//设置同步屏障之后,通过设置了Aysnc标记位的Handler发送的Msg都是异步消息,
//MessageQueue也优先处理此类异步消息,直到移除同步屏障标记位,再恢复到普通消息队列。

由此可见,同步屏障的设置和View刷新机制有关,因为要保证Vsync信号按时完成刷新操作,具体分析待续…
综述,异步消息可以保证精准的执行,但也因此消息事件的先后顺序被打乱,有可能在代码执行中执行了Handler.sendMsg(1,time0.2)->AsyncHandler.sendMsg(2,time0.5),但是实际执行的是 2->1。

再看Handler
Handler的成员变量如下

mLooper :初始化时获取当前线程的Looper对象引用 mQueue :通过Looper.mQueue 获取到的MessageQueue队列引用mAsynchronous :标记当前Handler是否发送异步消息 mCallback : Handler自身的callback接口,此callback调用在Message.callback之前mMessenger :IMessager 和进程通信相关

以上成员变量大都是final类型,表示Handler也是在其使用上也是final类型,也就是说,没有办法通过将Handler与context的生命周期相剥离来避免内存泄漏

Handler的方法如下

    //Handler 发送Message第一种方法,设置Message的what,data
    //不设置 runnable:Callback
    boolean sendMessage(Message msg) -> boolean sendMessageDelayed(Message msg,long delayTime)
    -> boolean sendMessageAtTime(Message msg,SystemClock.uptimeMillis()+delayTime)
    -> mQueue.enqueueMessage(msg,uptime)
    //第二种方法,Message只设置runnable:Callback
    boolean postAtTime(Runnable r,Object token,long uptime)
    -> sendMessageAtTime(getPostMessage(r,token),uptime)
    --> Message getPostMessage(Runnable r,Object token){
        Message.obtain().callback=r
        ...
        }
   //移除Message和检验Message
   removeMessages()
   hasMessages()
   ...
   //Message 回调执行
   void dispatchMessage(Message msg){
       if(msg.callback!=null){
           handleCallback(msg) ->
       }else{
           if(mCallback!=null){
            mCallback.handleMessage(msg)
           }
           handleMessage(msg)
   }
   //可以看到 Message的回调分为三个等级
   //No.1 msg自身的callback
   //No.2 Handler自身的mCallback成员变量,mCallback是final类型
   //No.3 Handler的子类重载的handleMessage方法

Message

Message 实现了Parcelable接口,也就是说可以作为进程间通信的载体

Message成员变量如下

    int what //Handler发送主体下的Message的消息码
    int arg1 //低成本的参数传递
    int arg2
    Object obj //可以为空的token对象,一般在进程通信中用到
    Bundle data //线程通信中常用的参数容器
    Handler target //发送主体
    Runnable callback //Message自身回调
    Messenger replyTo   //进程通信,一般在AMS中用到
    ------
    // Message缓存池相关
    Object sPoolSync = new Object() // 同步标记
    Messsage next
    static Message sPool
    static int sPoolSize

Message方法如下

    //可以看出这是一个非常巧妙的方法
    static Message obtain(){
        synchronized(sPoolsSync){
            if(sPools!=null){
                Message m= sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
              }
        }
        return new Message();
    }
    //主体上是一个带缓存池链表的同步工厂模式,同时也考虑到较多线程阻塞时
    //可以直接声明初始化对象

    //回收Message对象到缓存池链表
    void recycleUnchecked(){
        ...参数=null
        synchronized(sPoolSync){
            if(sPoolSize < MAX_SIZE){
                next = sPool;
                sPools = this;
                sPoolSize++;
          }
       }
    }

到此这篇关于Android Handler机制详解原理的文章就介绍到这了,更多相关Android Handler内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解Android Handler的使用

    目录 Handler 概要 构造器 sendMessageAtTime dispatchMessage ThreadLocal Looper MessageQueue IdleHandler AsyncMessage和SyncBarrier 阻塞和唤醒机制 Handler内存泄漏分析 Handler 概要 Handler用于线程间的消息传递,它可以将一个线程中的任务切换到另一个线程执行.切换的目标线程与Handler内部持有的Looper所在线程一致.若初始化Handler时未手动设置Loope

  • Android消息机制Handler深入理解

    目录 概述 Handler的使用 Handler架构 Handler的运行流程 源码分析 在子线程创建Handler 主线程的Looper Looper Handler 分发消息 总结 概述 Handler是Android消息机制的上层接口.通过它可以轻松地将一个任务切换到Handler所在的线程中去执行.通常情况下,Handler的使用场景就是更新UI. Handler的使用 在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI. public class Activity e

  • Android消息机制Handler用法总结

    1.简述 Handler消息机制主要包括: MessageQueue. Handler. Looper.Message. Message:需要传递的消息,可以传递数据: MessageQueue:消息队列,但是它的内部实现并不是用的队列,而是通过单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势.主要功能是向消息池投递消息( MessageQueue.enqueueMessage)和取走消息池的消息( MessageQueue.next).  Handler:消息辅助类,主要功能

  • Android Handler消息机制分析

    目录 Handler是什么? Handler 的基本使用 用法一:通过 send 方法 用法二:通过 post 方法 Handler 类 MessageQueue 类 Looper 类 Handler 的消息接收过程 Handler是什么? Handler 是一个可以实现多线程间切换的类,通过 Handler 可以轻松地将一个任务切换到 Handler 所在的线程中去执行.我们最常用的使用的场景就是更新 UI 了,比如我们在子线程中访问网络,拿到数据后我们 UI 要做一些改变,如果此时我们直接访

  • Android Handler使用案例详解

    什么是Handler? Handler可以发送和处理消息对象或Runnable对象,这些消息对象和Runnable对象与一个线程相关联.每个Handler的实例都关联了一个线程和线程的消息队列.当创建了一个Handler对象时,一个线程或消息队列同时也被创建,该Handler对象将发送和处理这些消息或Runnable对象. handler类有两种主要用途: 执行Runnable对象,还可以设置延迟. 两个线程之间发送消息,主要用来给主线程发送消息更新UI. 为什么要用Handler 解决多线程并

  • Android Handler机制详解原理

    Looper是整个跨线程通信的管理者 // 内部持有的变量如下: ThreadLocal<Looper> MainLooper Observer MessageQueue Thread 1.首先先回忆一下Handler怎么用 Android线程通信分为以下两种情况 1.子线程发消息给UI线程 2.UI线程发消息给子线程 3.子线程发消息给另个子线程 1.子线程发消息给UI线程 class FragmentContentActivity : AppCompatActivity() { val F

  • Android Handler的详解及实例

    Android Handler的详解 Handler我们常常用于通知主线程做相对应的操作,但是如果使用不但的话就会造成内存泄露,所以记录写正确的Handler写法. Handler handler = new Handler() { public void handleMessage(Message msg) { //do something }; }; handler.sendEmptyMessageDelayed(0, 100 * 1000); 像上面的代码片段,就会存在内存泄露的风险,因为

  • Android Handler机制的工作原理详析

    写在前面 上一次写完Binder学习笔记之后,再去看一遍Activity的启动流程,因为了解了Binder的基本原理,这次看印象会更深一点,学习效果也比以前好很多.本来打算直接来写Activity的启动流程的,但总觉得Handler也需要写一下,知道Handler和Binder的原理后,再去看Activity的启动流程,应该也没什么问题了.虽然网上已经有很多Handler相关的文章了,而且Handler机制的上层原理也并不难,还是决定写一下,因为我想构建自己的知识体系.也希望给看我博客的朋友们一

  • Android Handler多线程详解

    Android--多线程之Handler 前言 Android的消息传递机制是另外一种形式的"事件处理",这种机制主要是为了解决Android应用中多线程的问题,在Android中不 允许Activity新启动的线程访问该Activity里的UI组件,这样会导致新启动的线程无法改变UI组件的属性值.但实际开发中,很多地方需要在 工作线程中改变UI组件的属性值,比如下载网络图片.动画等等.本篇博客主要介绍Handler是如何发送与处理线程上传递来的消息,并讲解 Message的几种传递数

  • Android 消息机制详解及实例代码

    Android 消息机制 1.概述 Android应用启动时,会默认有一个主线程(UI线程),在这个线程中会关联一个消息队列(MessageQueue),所有的操作都会被封装成消息队列然后交给主线程处理.为了保证主线程不会退出,会将消息队列的操作放在一个死循环中,程序就相当于一直执行死循环,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数(handlerMessage),执行完成一个消息后则继续循环,若消息队列为空,线程则会阻塞等待.因此不会退出.如下图所示: Handl

  • 详解Android Handler机制和Looper Handler Message关系

    概述 我们就从以下六个问题来探讨Handler 机制和Looper.Handler.Message之前的关系? 1.一个线程有几个Handler? 2.一个线程有几个Looper?如何保证? 3.Handler内存泄漏原因?为什么其他的内部类没有说过这个问题? 4.为何主线程可以new Handler?如果在想要在子线程中new Handler 要做些什么准备? 5.子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用? 6.Looper死循环为什么不会导致应用卡死? 一.

  • Android 进阶实现性能优化之OOM与Leakcanary详解原理

    目录 Android内存泄漏常见场景以及解决方案 资源性对象未关闭 注册对象未注销 类的静态变量持有大数据 单例造成的内存泄漏 非静态内部类的静态实例 Handler临时性内存泄漏 容器中的对象没清理造成的内存泄漏 WebView 使用ListView时造成的内存泄漏 Leakcanary leakcanary 导入 leakcanary 是如何安装的 leakcanary 如何监听Activity.Fragment销毁 RefWatcher 核心原理 流程图 本文主要探讨以下几个问题: And

  • Android线程间通信 Handler使用详解

    目录 前言 01.定义 02.使用 第一步.创建 第二步.发送消息 第一种是 post(Runnable) 第二种是 sendMessage(Message) 第三步.处理消息 03.结语 前言 Handler,可谓是面试题中的一个霸主了.在我<面试回忆录>中,几乎没有哪家公司,在面试的时候是不问这个问题的.简单一点,问问使用流程,内存泄漏等问题.复杂一点,纠其源码细节和底层 epoll 机制来盘你.所以其重要性,不言而喻了吧. 那么今天让我们来揭开 Handler 的神秘面纱.为了读者轻松易

  • Android NTP 时间同步机制详解

    目录 正文 初始化 NetworkTimeUpdateCallback AutoTimeSettingObserver MyHandler onPollNetworkTime 总结 正文 NTP是Android原生通过网络获取时间的机制,其中关键代码逻辑都在NetworkTimeUpdateService,它是Android系统服务,由SystemServer启动. 本篇文章基于Android 10源码分析. 初始化 我们从它的构造方法开始分析: public NetworkTimeUpdate

  • Android开发App启动流程与消息机制详解

    目录 引言 1.第一步了解 ThreadLocal 2.App的启动流程 3.Activity中创建Handler 引言 相信很多人对这个问题不陌生,但是大家回答的都比较简单,如谈到app启动流程有人就会是app的生命周期去了,谈到消息机制有人就会说looper循环消息进行分发,如果是面试可能面试官不会满意,今天我们搞一篇完善的源码解析来进行阐述上面的问题 1.第一步了解 ThreadLocal 什么是ThreadLocal呢,专业的来讲,ThreadLocal 是一个线程内部的数据存储类,通过

随机推荐