FW Activity跳转动画源码解析(一)

跳转动画实际操作的是什么?

startActivity调用之后进行页面跳转,会有一系列的涉及到ActivitStar,ActivityTask,ActivityManager等类的操作,最终在执行动画会调用到SurfaceControl中去,相关代码如下

public final class SurfaceControl implements Parcelable {
//省略代码
    @NonNull
        public Transaction setAlpha(@NonNull SurfaceControl sc,
                @FloatRange(from = 0.0, to = 1.0) float alpha) {
            checkPreconditions(sc);
            nativeSetAlpha(mNativeObject, sc.mNativeObject, alpha);
            return this;
        }
//省略代码
 @UnsupportedAppUsage
        public Transaction setMatrix(SurfaceControl sc, Matrix matrix, float[] float9) {
            matrix.getValues(float9);
            setMatrix(sc, float9[MSCALE_X], float9[MSKEW_Y],
                    float9[MSKEW_X], float9[MSCALE_Y]);
            setPosition(sc, float9[MTRANS_X], float9[MTRANS_Y]);
            return this;
        }
//这个是调用到native的函数        
 @UnsupportedAppUsage
        public Transaction setMatrix(SurfaceControl sc,
                float dsdx, float dtdx, float dtdy, float dsdy) {
            checkPreconditions(sc);
            nativeSetMatrix(mNativeObject, sc.mNativeObject,
                    dsdx, dtdx, dtdy, dsdy);
            return this;
        }
  @UnsupportedAppUsage
        public Transaction setPosition(SurfaceControl sc, float x, float y) {
            checkPreconditions(sc);
            nativeSetPosition(mNativeObject, sc.mNativeObject, x, y);
            return this;
        }
//省略代码
}

以上函数就负责了窗口的透明度以及大小,还有位置的变换.
可以看到相应的设置函数,最终都会调用到native层去,我们选一个cpp的代码看一下

//android_view_SurfaceControl.cpp
static void nativeSetPosition(JNIEnv* env, jclass clazz, jlong transactionObj,
        jlong nativeObject, jfloat x, jfloat y) {
    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);

    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
    transaction->setPosition(ctrl, x, y);
}

//SurfaceComposerClient.cpp
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setPosition(
        const sp<SurfaceControl>& sc, float x, float y) {
    layer_state_t* s = getLayerState(sc);
    if (!s) {
        mStatus = BAD_INDEX;
        return *this;
    }
    s->what |= layer_state_t::ePositionChanged;
    s->x = x;
    s->y = y;

    registerSurfaceControlForCallback(sc);
    return *this;
}

以上代码调用到native去了之后会把相关设置的值赋值给layer,如果需要再cpp中定制动画相关的设置
可以修改这一块.

关于SurfaceControl 的调用链路如下图,WindowContainer,ActivityRecord大家应该都不陌生
在这里插入图片描述

窗口怎么知道应该执行什么动画,是透明,还是平移,还是缩放,旋转?

class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
        implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
                   BLASTSyncEngine.TransactionReadyListener {

    private Animation loadAnimation(WindowManager.LayoutParams lp, int transit, boolean enter,
                                    boolean isVoiceInteraction) {
          //省略代码
          //通过Transition获取动画对象
               final Animation a = getDisplayContent().mAppTransition.loadAnimation(lp, transit, enter,
                displayConfig.uiMode, displayConfig.orientation, frame, displayFrame, insets,
                surfaceInsets, stableInsets, isVoiceInteraction, inFreeformWindowingMode(), this);
          //省略代码
	}                   
}

public class AppTransition implements Dump {
    Animation loadAnimation(LayoutParams lp, int transit, boolean enter, int uiMode,
            int orientation, Rect frame, Rect displayFrame, Rect insets,
            @Nullable Rect surfaceInsets, @Nullable Rect stableInsets, boolean isVoiceInteraction,
            boolean freeform, WindowContainer container) {
            //这里面有一堆判定,但是普通应用的时候是不会通过的,会再调用到loadAnimationAttr
              a = animAttr != 0 ? loadAnimationAttr(lp, animAttr, transit) : null;
              return a;
            }
    
    //调用getCachedAnimations获取Entry
    Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) {
        int resId = Resources.ID_NULL;
        Context context = mContext;
        if (animAttr >= 0) {
            AttributeCache.Entry ent = getCachedAnimations(lp);
            if (ent != null) {
                context = ent.context;
                resId = ent.array.getResourceId(animAttr, 0);
            }
        }
        resId = updateToTranslucentAnimIfNeeded(resId, transit);
        if (ResourceId.isValid(resId)) {
            return loadAnimationSafely(context, resId);
        }
        return null;
    }
	//获取Styleable中的动画主题
    private AttributeCache.Entry getCachedAnimations(LayoutParams lp) {
        if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: layout params pkg="
                + (lp != null ? lp.packageName : null)
                + " resId=0x" + (lp != null ? Integer.toHexString(lp.windowAnimations) : null));
        if (lp != null && lp.windowAnimations != 0) {
            // If this is a system resource, don't try to load it from the
            // application resources.  It is nice to avoid loading application
            // resources if we can.
            String packageName = lp.packageName != null ? lp.packageName : "android";
            int resId = getAnimationStyleResId(lp);
            if ((resId&0xFF000000) == 0x01000000) {
                packageName = "android";
            }
            if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
                    + packageName);
            return AttributeCache.instance().get(packageName, resId,
                    com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
        }
        return null;
    }
}

在执行以上代码以后,就会从主题中获取到定义的窗口切换动画资源了.
相关的调用栈如下:
在这里插入图片描述

  • 获取到默认的启动动画资源,这个会对应一个anim的xml文件,以下文件位于frameworks/base/core/res/res/anim路径.
    android:anim/activity_open_enter

  • 退出的时候默认动画如下
    android:anim/activity_close_enter

在获取到以上资源以后动画会调用到WindowAnimationSpec类中去执行如下代码


    @Override
    public void apply(Transaction t, SurfaceControl leash, long currentPlayTime) {
        final TmpValues tmp = mThreadLocalTmps.get();
        tmp.transformation.clear();
        mAnimation.getTransformation(currentPlayTime, tmp.transformation);
        tmp.transformation.getMatrix().postTranslate(mPosition.x, mPosition.y);
        t.setMatrix(leash, tmp.transformation.getMatrix(), tmp.floats);
        t.setAlpha(leash, tmp.transformation.getAlpha());

        boolean cropSet = false;
        if (mStackClipMode == STACK_CLIP_NONE) {
            if (tmp.transformation.hasClipRect()) {
                t.setWindowCrop(leash, tmp.transformation.getClipRect());
                cropSet = true;
            }
        } else {
            mTmpRect.set(mStackBounds);
            if (tmp.transformation.hasClipRect()) {
                mTmpRect.intersect(tmp.transformation.getClipRect());
            }
            t.setWindowCrop(leash, mTmpRect);
            cropSet = true;
        }

        // We can only apply rounded corner if a crop is set, as otherwise the value is meaningless,
        // since it doesn't have anything it's relative to.
        if (cropSet && mAnimation.hasRoundedCorners() && mWindowCornerRadius > 0) {
            t.setCornerRadius(leash, mWindowCornerRadius);
        }
    }

以上代码中 mAnimation.getTransformation(currentPlayTime, tmp.transformation);这一行就是通过之前得到的动画对象去计算应该设置的透明,平移等参数值,以用于去设置视图.
简单看一下这个计算吧,感兴趣的可以深入研究一下

public abstract class Animation implements Cloneable {

   public boolean getTransformation(long currentTime, Transformation outTransformation) {
        if (mStartTime == -1) {
            mStartTime = currentTime;
        }

        final long startOffset = getStartOffset();
        final long duration = mDuration;
        float normalizedTime;
        if (duration != 0) {
            normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
                    (float) duration;
        } else {
            // time is a step-change with a zero duration
            normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
        }

        final boolean expired = normalizedTime >= 1.0f || isCanceled();
        mMore = !expired;

        if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);

        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
            if (!mStarted) {
                fireAnimationStart();
                mStarted = true;
                if (NoImagePreloadHolder.USE_CLOSEGUARD) {
                    guard.open("cancel or detach or getTransformation");
                }
            }

            if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);

            if (mCycleFlip) {
                normalizedTime = 1.0f - normalizedTime;
            }

            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
            applyTransformation(interpolatedTime, outTransformation);
        }

        if (expired) {
            if (mRepeatCount == mRepeated || isCanceled()) {
                if (!mEnded) {
                    mEnded = true;
                    guard.close();
                    fireAnimationEnd();
                }
            } else {
                if (mRepeatCount > 0) {
                    mRepeated++;
                }

                if (mRepeatMode == REVERSE) {
                    mCycleFlip = !mCycleFlip;
                }

                mStartTime = -1;
                mMore = true;

                fireAnimationRepeat();
            }
        }

        if (!mMore && mOneMoreTime) {
            mOneMoreTime = false;
            return true;
        }

        return mMore;
    }

}

以上在各种计算之后再调用到子类的applyTransformation函数去赋值给传入进来的对象Transformation outTransformation,赋值完了之后,后续的动画都会依赖这个计算出来的值进行设置了.

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C知道:当涉及到OpenSBI源码解析时,主要关注的是它作为开源固件的实现,用于支持RISC-V架构的启动过程。OpenSBI是一个与特定硬件平台无关的软件层,它提供了一系列的功能和接口,可以用于初始化硬件、配置环境和启动操作系统。 OpenSBI的源码位于GitHub上,你可以在https://github.com/riscv/opensbi找到它。我将为你提供一个大致的概述,但请注意,这只是一个简化的解释。 OpenSBI的主要组件包括平台代码、运行时服务和扩展库。平台代码是与特定硬件平台相关的部分,它包含了对硬件的初始化、配置和访问的代码。运行时服务是OpenSBI的核心部分,它提供了与操作系统交互的接口,并处理异常、中断等事件。扩展库则提供了一些额外的功能和工具。 具体来说,OpenSBI的源码包括以下几个重要文件和目录: - `include/`:包含了各种头文件,定义了OpenSBI的接口和数据结构。 - `platform/`:包含了平台代码,每个硬件平台都有对应的目录。 - `fw_payload/`:包含了启动负载代码,用于加载和启动操作系统。 - `lib/`:包含了运行时服务和扩展库的实现代码。 - `plat/`:包含了平台相关的实现代码,用于初始化和配置硬件。 - `drivers/`:包含了与硬件设备相关的驱动代码。 - `include/sbi/`:包含了OpenSBI的标准接口定义。 通过阅读这些源码文件,你可以深入了解OpenSBI的实现细节和工作原理。注意,OpenSBI是一个相对复杂的项目,理解它需要对RISC-V架构和底层硬件有一定的了解。 希望这个简要的解析能帮助你开始理解OpenSBI的源码。如果你有具体的问题或需要更深入的讨论,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值