invalidate()是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。 一个Android 程序默认情况下也只有一个进程,但一个进程下却可以有许多个线程。
在这么多线程当中,把主要是负责控制UI界面的显示、更新和控件交互的线程称为UI线程,由于onCreate()方法是由UI线程执行的,所以也可以把UI线程理解为主线程。其余的线程可以理解为工作者线程。
invalidate()得在UI线程中被调动,在工作者线程中可以通过Handler来通知UI线程进行界面更新。
Android提供了Invalidate方法实现界面刷新,但是Invalidate不能直接在线程中调用,因为他是违背了单线程模型:Android UI操作并不是线程安全的,并且这些操作必须在UI线程中调用。
简单说invalidate是在ui线程中使用的。相应的,postinvalidate是在非ui线程里面使用的比如说你用handler刷新,不开新的线程,用invalidate就行了。如果你加载了另外一个线程,就要用postinvalidate。
主要原因是Activity的ui刷新基于单线程模型,非ui线程上的ui操作是不允许的,会报错。所以在非ui线程上用postinvalidate给ui线程一个讯号,让ui线程进行刷新操作
使用postInvalidate比较简单,不需要handler,直接在线程中调用postInvalidate即可,除了onCreate()不是运行在UI线程上的,其实其他大部分方法都是运行在UI线程上的,其实只要你没有开启新的线程,你的代码基本上都运行在UI线程上
再来看postInvalidateOnAnimation的执行过程
InvalidateOnAnimationRunnable本质上是一个Runnable ,run()方法中实际执行view.invalidate方法,也就是说现在的问题是run()怎么执行的,什么时候执行的?!
这里将InvalidateOnAnimationRunnable 做为参数传给了mChoreographer对象,也就是这里的逻辑就完成了,剩下的invalidate任务交给了mChoreographer。
其中mPosted为true时表示已经通知了mChoreographer来处理刷新的任务,但任务还没有真正执行,也就在有新的view刷新任务添加时,不再重复通知了。
c.run(frameTimeNanos);这里也就是对应InvalidateOnAnimationRunnable的run()方法。
c是CallbackRecord的实例,CallbackRecord里拥有InvalidateOnAnimationRunnable的实例,即下面代码中的action;
c.run(frameTimeNanos);中有个时间参数,但((Runnable)action).run();是没有用到的,可以不关心。
所以现在找定义执行该函数时间的地方。
doCallbacks被doFrame方法调用,doFrame方法执行是在handleMessage中:
而发送MSG_DO_FRAME消息的代码为:
那么执行时间就由nextFrameTime来决定了。也就是从当前时间开始的10ms内(理论情况)。
这里是没有使用Vsync同步机制的情况,使用Vsync会增加一些另外的操作,但是最终执行逻辑是一样的,详细的分析可参考Android系统Choreographer机制实现过程。
总结一下:
postInvalidate方法是将任务添加到队列中排队后立即执行的,而postInvalidateOnAnimation依赖上一帧动画的的执行时间,因为动画的刷新是存在一个频率的,直到下一帧动画的时间才会真正执行刷新操作。
而postIfNeededLocked()干的事情就是把mInvalidateOnAnimationRunnable作为Choreographer.CALLBACK_ANIMATION(这个类型的task会在mesaure/layout/draw之前被运行)的Task 交给 UI线程的Choreographer.
View的invalidate会进一步触发ViewRootImpl的invalidateChildInParent()->invalidate()<一种情况(dirty == null 表示全部重绘),不过另外一种差不多>:
不过虽然没有发出去,不代表这次invalidate就不会生效,因为前面的invalidate()里已经设置了mDirty了: