您的位置:首页 > 运维架构 > 网站架构

Android O Framework架构分析:属性动画ObjectAnimator机制分析

2018-02-13 15:36 477 查看
一. 概述
        紧接上一篇: Android O Framework架构分析:属性动画ValueAnimator机制分析,本文将介绍另一个常用的属性动画类型:ObjectAnimator.
        ObjectAnimator是ValueAnimator的子类,其整体动画驱动逻辑,属性值的计算方式和ValueAnimator完全相同。不同的地方在于ObjectAnimator可以将属性的每一帧的取值自动设置到传递的对象中。本文将介绍ObjectAnimator的实现架构。

        例如:private void testObjectAnimator() {
final ObjectAnimatorTest test = new ObjectAnimatorTest();
ObjectAnimator animator = ObjectAnimator.ofInt(test, "value", 0, 100);
animator.setDuration(100);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
Log.d(TAG, "test value: " + test.mValue);
}
});
animator.start();
}

class ObjectAnimatorTest {
int mValue;
private void setValue(int value) {
mValue = value;
}
}        其基本使用方式如上所示,ObjectAnimator同样提供了ofXXX方法进行初始化,不同的是参数发生了变化。上面例子中的三个参数分别代表:
        Object:属性值所作用到的对象(上例中的test)。
        String:属性名,在第一个参数的类中必须提供对应的setXXX方法(XXX为传递到这里的属性名),用来在每一帧更新该对象的这个成员变量的值。
        int...:关键帧,作用同ValueAnimator
        上面的例子输出结果为:
        02-12 17:31:49.331 12805 12805 D property_animator_test: test value: 0
        02-12 17:31:49.350 12805 12805 D property_animator_test: test value: 0
        02-12 17:31:49.364 12805 12805 D property_animator_test: test value: 6
        02-12 17:31:49.381 12805 12805 D property_animator_test: test value: 24
        02-12 17:31:49.397 12805 12805 D property_animator_test: test value: 50
        02-12 17:31:49.414 12805 12805 D property_animator_test: test value: 75
        02-12 17:31:49.431 12805 12805 D property_animator_test: test value: 93
        可以看到在每一帧的回调中,传入的对象对应的属性值都被自动地更新了。下面将基于Android O的版本详细介绍下ObjectAnimator在系统中的实现方式。
二. 基本流程分析
        以上例中的代码实现为例,其基本流程可以分为以下几个步骤:
1.动画初始化过程



        其初始化过程和ValueAnimator大体相同,唯一不同的是在创建ObjectAnimator需要将属性作用对象与属性名保存起来(step3 ~ step4)。上面省略了部分与ValueAnimator初始化相同的逻辑。
2.动画启动过程

        由于前面例子中没有设置对应的Property,而是通过传入Object和属性名进行初始化的,所以其时序图如下(需要通过jni层反射获取setter/getter):



        其主要流程为:
        step2:检查之前是否有作用与相同Object相同属性的动画,如果有,则取消之前的。
        step5:尝试获取传入Object的setXXX方法和getXXX方法(上图仅画出了setter获取流程,getter类似)。
        step6:尝试获取setter方法。
        step7 ~ step8:根据传入的属性名拼接对应的setter方法名,其逻辑为将属性名的第一个字母改成大写,然后和前缀"set"拼接起来。如前例所示例,传入的属性值为"value",则拼接出来的方法名为"setValue".
        step9 ~ step11:这里通过JNI获取对应方法名的jMethodID,默认通过JNI层反射调用对应的setter/getter方法。
        step12:调用基类的initAnimation方法,后续流程与ValueAnimator一致。
        这里省略了一段逻辑,如果通过jni层反射无法找到,则会通过Java层的反射找到对应的Method对象。后续也将通过Java层反射调用对应的setter/getter方法。
3.动画驱动流程

        同样由于前面例子中没有设置对应的Property,而是通过传入Object和属性名进行初始化的,所以其时序图如下(需要通过JNI层反射调用setter):



        ObjectAnimator的动画驱动逻辑与ValueAnimator一致,都是通过向Choreographer中注册动画回调来驱动的。
        其主要流程为:
        step1:Choreographer中回调属性动画注册的动画回调。
        step2 ~ step4:先执行基类(ValueAnimator)的流程,为了先计算出动画进度以及当前帧的属性取值。
        step5 ~ step7:这里已经计算出了属性取值,需要通过setter方法设置到传递进来的Object中。上图中依然是通过JNI反射去调用对应的setter方法的。如果JNI反射找不到该方法,则通过Java层反射去访问。

        动画驱动过程中仍然是基于ValueAnimator的逻辑,只不过在计算出属性取值后,需要将其主动通过传入的Object的setXXX方法设置进去。
三. 关键流程源码分析
        与ValueAnimator相比,ObjectAnimator主要的的功能在于,在动画驱动的每一帧里自动地为指定对象的属性赋值。首先需要找到对应的方法(PropertyValuesHolder.java):void setupSetterAndGetter(Object target) {
// 如果设置了Property则后续通过Property调用setter
if (mProperty != null) {
// check to make sure that mProperty is on the class of target
try {
// 初始化关键帧中的属性值(动画开始前的属性取值设置为传入对象该属性的默认值)
Object testValue = null;
List<Keyframe> keyframes = mKeyframes.getKeyframes();
int keyframeCount = keyframes == null ? 0 : keyframes.size();
for (int i = 0; i < keyframeCount; i++) {
Keyframe kf = keyframes.get(i);
if (!kf.hasValue() || kf.valueWasSetOnStart()) {
if (testValue == null) {
testValue = convertBack(mProperty.get(target));
}
kf.setValue(testValue);
kf.setValueWasSetOnStart(true);
}
}
return;
} catch (ClassCastException e) {
Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
") on target object " + target + ". Trying reflection instead");
mProperty = null;
}
}
// We can't just say 'else' here because the catch statement sets mProperty to null.
// 没有设置Property,则需要先找到对应Object的setter/getter方法
if (mProperty == null) {
Class targetClass = target.getClass();
if (mSetter == null) {
// 尝试根据属性名获取setter方法
setupSetter(targetClass);
}
List<Keyframe> keyframes = mKeyframes.getKeyframes();
int keyframeCount = keyframes == null ? 0 : keyframes.size();
for (int i = 0; i < keyframeCount; i++) {
Keyframe kf = keyframes.get(i);
if (!kf.hasValue() || kf.valueWasSetOnStart()) {
if (mGetter == null) {
// 尝试根据属性名获取getter方法
setupGetter(targetClass);
if (mGetter == null) {
// Already logged the error - just return to avoid NPE
// 如果客户端没有提供getter方法,则直接返回,不对关键帧进行初始化
return;
}
}
try {
// 初始化关键帧中的属性值(动画开始前的属性取值设置为传入对象该属性的默认值)
Object value = convertBack(mGetter.invoke(target));
kf.setValue(value);
kf.setValueWasSetOnStart(true);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
}
}        上面可以看到,在尝试获取setter/getter方法时的逻辑为:
        (1) 客户端是否传入了Property对象,如果传入,则setter/getter都被客户端封装在了Property的set/get方法中,后面直接调用即可。
        (2) 没有传入Property对象,则需要根据属性名查询。
        本文一开始的例子是传入的int型属性,所以在查询setter方法时将会调用PropertyValuesHolder的子类:
        IntPropertyValuesHolder#setupSetter方法:
@Override
void setupSetter(Class targetClass) {
// 如果客户端自己传递进来了Property,则需要自己实现Property中的set/get方法
// 通过Property中的set/get包装对应Object的setter/getter方法
// 此时在此处不需要获取setter了,直接返回
if (mProperty != null) {
return;
}
// Check new static hashmap<propName, int> for setter method
synchronized(sJNISetterPropertyMap) {
// sJNISetterPropertyMap是一个property name -> setter的缓存,防止重复查找
HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
boolean wasInMap = false;
// 查看缓存里是否已经有setter了,如果有,则不需要重新查找了
if (propertyMap != null) {
wasInMap = propertyMap.containsKey(mPropertyName);
if (wasInMap) {
Long jniSetter = propertyMap.get(mPropertyName);
if (jniSetter != null) {
mJniSetter = jniSetter;
}
}
}
// 缓存中没有,则需要重新查找setter
if (!wasInMap) {
// 拼接对应的setter方法名,逻辑为将属性名第一个字母变成大写,然后与前缀set进行拼接
String methodName = getMethodName("set", mPropertyName);
try {
// 通过JNI层的反射查找对应的方法
mJniSetter = nGetIntMethod(targetClass, methodName);
} catch (NoSuchMethodError e) {
// Couldn't find it via JNI - try reflection next. Probably means the method
// doesn't exist, or the type is wrong. An error will be logged later if
// reflection fails as well.
}
// 将查询结果记录在缓存中
if (propertyMap == null) {
propertyMap = new HashMap<String, Long>();
sJNISetterPropertyMap.put(targetClass, propertyMap);
}
propertyMap.put(mPropertyName, mJniSetter);
}
}
// 如果JNI层反射找不到,则使用Java层反射查找
if (mJniSetter == 0) {
// Couldn't find method through fast JNI approach - just use reflection
super.setupSetter(targetClass);
}
}
        上面通过nGetIntMethod方法调用到JNI层(android_animation_PropertyValuesHolder.cpp):static jlong android_animation_PropertyValuesHolder_getIntMethod(
JNIEnv* env, jclass pvhClass, jclass targetClass, jstring methodName)
{
const char *nativeString = env->GetStringUTFChars(methodName, 0);
// 获取对应的方法ID
jmethodID mid = env->GetMethodID(targetClass, nativeString, "(I)V");
env->ReleaseStringUTFChars(methodName, nativeString);
// 将其转化为Java层的long类返回给Java层
return reinterpret_cast<jlong>(mid);
}        基类的setupSetter方法是通过Java层反射进行的(PropertyValuesHolder.java):void setupSetter(Class targetClass) {
Class<?> propertyType = mConverter == null ? mValueType : mConverter.getTargetType();
mSetter = setupSetterOrGetter(targetClass, sSetterPropertyMap, "set", propertyType);
}

private Method setupSetterOrGetter(Class targetClass,
HashMap<Class, HashMap<String, Method>> propertyMapMap,
String prefix, Class valueType) {
Method setterOrGetter = null;
synchronized(propertyMapMap) {
// Have to lock property map prior to reading it, to guard against
// another thread putting something in there after we've checked it
// but before we've added an entry to it
HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
boolean wasInMap = false;
// 先从缓存里找
if (propertyMap != null) {
wasInMap = propertyMap.containsKey(mPropertyName);
if (wasInMap) {
setterOrGetter = propertyMap.get(mPropertyName);
}
}
// 如果不在缓存里面,则通过Java层反射,获取对应的Method对象记录下来
if (!wasInMap) {
setterOrGetter = getPropertyFunction(targetClass, prefix, valueType);
if (propertyMap == null) {
propertyMap = new HashMap<String, Method>();
propertyMapMap.put(targetClass, propertyMap);
}
propertyMap.put(mPropertyName, setterOrGetter);
}
}
return setterOrGetter;
}        当动画驱动时,首先根据ValueAnimator的驱动逻辑,会计算出这一帧的属性取值。在ObjectAnimator中会把这一帧的属性取值通过前面获取的setter方法设置到传入的Object中。前面例子中设置的是int类型的属性,所以这里会调用到
        IntPropertyValuesHolder#setAnimatedValue:@Override
void setAnimatedValue(Object target) {
// 客户端是否传入了IntProperty对象,如果传入了,则setter被包装在其setValue方法中
// 直接调用后返回
if (mIntProperty != null) {
mIntProperty.setValue(target, mIntAnimatedValue);
return;
}
// 客户端是否传入了Property对象,如果传入了,则setter被包装在其set方法中
// 直接调用后返回
if (mProperty != null) {
mProperty.set(target, mIntAnimatedValue);
return;
}
// 前面是否通过了JNI层反射找到了setter方法,如果找到了,则再次通过JNI层方法调用对应的setter
if (mJniSetter != 0) {
nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
return;
}
// 如果前面都不满足,则使用Java层反射调用
if (mSetter != null) {
try {
mTmpValueArray[0] = mIntAnimatedValue;
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}        本例子中没有传入Property,则应该通过JNI层反射进行调用(android_animation_PropertyValuesHolder.cpp):static void android_animation_PropertyValuesHolder_callIntMethod(
JNIEnv* env, jclass pvhObject, jobject target, jlong methodID, jint arg)
{
env->CallVoidMethod(target, reinterpret_cast<jmethodID>(methodID), arg);
}        其余的动画机制和ValueAnimator中一致。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息