时间:2022-10-25 09:32:49 | 栏目:Android代码 | 点击:次
Looper是整个跨线程通信的管理者
// 内部持有的变量如下: ThreadLocal<Looper> MainLooper Observer MessageQueue Thread
Android线程通信分为以下两种情况
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") } }) } } }
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++; } } }