前言
这篇博文是为了解决一个问题,在之前的博客已经提过了,就是属性动画的执行结束的回调,不是在主线程。
首先,阅读这篇文章,大家一定要有一个基础,就是,android不一定只能在主线程才能更新UI。这方面我不多说,想多了解的同学大家自己去查阅博客。
下面回到我们这个问题,在探究源码前,我们先来验证一下
(多说一句,android动画相当在计算位置坐标的时候相当蛋疼,所以楼主一直不愿意做涉及到ui的动画,但是ConstraintLayout给了一个直接通过xml可以执行的动画,相当爽,晚点咱们再聊聊)
代码很简单,就不写了,就是在普通的线程里面,执行动画,毫无意外崩溃了,接下来再看看报错
没错,报错了,但是,提示的却不是必须在UI线程调用,而是,线程里面必须又Looper?
大家有没有想到,哪个线程里面是有Looper的呢?没错,就是HandlerThread,这是google给我封装好的线程,我们来试试。
val mhandler = Handler(handlerT.looper);
mhandler.post() {
iv.animate().scaleX(2f).scaleY(2f).setDuration(1000)
Thread.sleep(100000)
// ObjectAnimator.ofInt(constraintlayou_root, "translationX", 0, 100, 0, 100)
// .setDuration(3000)
// .start();
}
毫不意外,这里顺利运行了。关于animate()大家可以了解下,是Google给我们封装好的动画,很好用。并且,大家可以发现,我再这个线程里面调用了,Thread.sleep,但是,动画完全没有任何影响。这是为什么呢?
下面就要进入我们的源码分析时间了。
先给出一个写的比较好的博客
Android 属性动画详解与源码分析。
但这这个博客存在一个问题,就是分析到jni或者反射调用后就没有接着往下分析了。所以楼主不会从0开始继续分析这个地方,而是调出关键节点,梳理清楚,这样在很长一段时间后,不需要看长篇大论的源码,就能快速回忆起关键的地方。
首先,略过中间部分不谈,最关键的,就是,通过jni或者反射调用到scaleX之后,又发生了什么。
这里给一个debug栈的截图
可以看出,这里走的是jni调用。然后走到了invalidateViewProperty(),该函数如下
/**
* Quick invalidation for View property changes (alpha, translationXY, etc.). We don't want to
* set any flags or handle all of the cases handled by the default invalidation methods.
* Instead, we just want to schedule a traversal in ViewRootImpl with the appropriate
* dirty rect. This method calls into fast invalidation methods in ViewGroup that
* walk up the hierarchy, transforming the dirty rect as necessary.
*
* The method also handles normal invalidation logic if display list properties are not
* being used in this view. The invalidateParent and forceRedraw flags are used by that
* backup approach, to handle these cases used in the various property-setting methods.
*
* @param invalidateParent Force a call to invalidateParentCaches() if display list properties
* are not being used in this view
* @param forceRedraw Mark the view as DRAWN to force the invalidation to propagate, if display
* list properties are not being used in this view
*/
@UnsupportedAppUsage
void invalidateViewProperty(boolean invalidateParent, boolean forceRedraw) {
if (!isHardwareAccelerated()
|| !mRenderNode.hasDisplayList()
|| (mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {
if (invalidateParent) {
invalidateParentCaches();
}
if (forceRedraw) {
mPrivateFlags |= PFLAG_DRAWN; // force another invalidation with the new orientation
}
invalidate(false)