前面的文章说过,AppTransition代表了activity组件的切换过程,其实解锁也是一种AppTrasition,只不过需要隐藏的activity组件是锁屏窗口罢了(严格来说并不是锁屏窗口被隐藏了,它只是调整了窗口的大小及相关参数,最后变成状态栏),解锁过程的窗口变化主要是以下几点

改变锁屏窗口状态
显示锁屏下方的窗口
播放解锁动画
1.解锁过程的appTransition
解锁过程的核心实质上是锁屏启动了一个runnable,通知AMS和WMS显示锁屏下方的activity组件窗口以及调用该activity组件的生命周期,向AMS和WMS发送命令的时候会传递一些flag,这些flag和解锁的场景有关,一般来说我们只用关注第一个,即WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS,例如熄屏的时候使用指纹解锁亮屏,这期间不需要显示解锁动画,于是就需要传递这个消息给WMS

private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {
@Override
public void run() {
try {
mStatusBarKeyguardViewManager.keyguardGoingAway();
int flags = 0;
if (mStatusBarKeyguardViewManager.shouldDisableWindowAnimationsForUnlock()
|| mWakeAndUnlocking) {
flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS;
}
if (mStatusBarKeyguardViewManager.isGoingToNotificationShade()) {
flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
}
if (mStatusBarKeyguardViewManager.isUnlockWithWallpaper()) {
flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
}
ActivityManagerNative.getDefault().keyguardGoingAway(flags);
} catch (RemoteException e) {
Log.e(TAG, “Error while calling WindowManager”, e);
}
}
};
接着看看AMS以及WMS的具体实现

AMS
public void keyguardGoingAway(int flags) {
enforceNotIsolatedCaller(“keyguardGoingAway”);
final long token = Binder.clearCallingIdentity();
try {
synchronized (this) {
if (DEBUG_LOCKSCREEN) logLockScreen("");
mWindowManager.keyguardGoingAway(flags);
if (mLockScreenShown == LOCK_SCREEN_SHOWN) {
mLockScreenShown = LOCK_SCREEN_HIDDEN;
updateSleepIfNeededLocked(); // 执行apptransition

                // Some stack visibility might change (e.g. docked stack)mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); //apptransition解锁后调整activitystackapplyVrModeIfNeededLocked(mFocusedActivity, true);}}} finally {Binder.restoreCallingIdentity(token);}
}

WMS
public void keyguardGoingAway(int flags) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException(“Requires DISABLE_KEYGUARD permission”);
}
if (DEBUG_KEYGUARD) Slog.d(TAG_WM,
“keyguardGoingAway: flags=0x” + Integer.toHexString(flags));
synchronized (mWindowMap) {
mAnimator.mKeyguardGoingAway = true;
mAnimator.mKeyguardGoingAwayFlags = flags;
mWindowPlacerLocked.requestTraversal();
}
}
可以看到AMS的主要作用是启动apptransition和向WMS发送消息,WMS接收到消息后就会把flag传递给负责管理窗口动画的WindowAnimator对象mAnimator,同时刷新系统UI

2.解锁过程的窗口动画
AppTransition的过程前面的文章有说,这里主要关注解锁过程的窗口动画

2.1activity组件切换动画
设置activity组件的切换动画会调到如下函数,其中,wtoken.hidden表示activity组件是否应该处于hidden状态,然而对于解锁后需要打开的activity组件来说,hidden = false,而visibility = true,所以实际上解锁过程是不会设置transition动画

boolean setTokenVisibilityLocked(AppWindowToken wtoken, WindowManager.LayoutParams lp,
boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {
boolean delayed = false;

boolean visibilityChanged = false;
if (wtoken.hidden == visible || (wtoken.hidden && wtoken.mIsExiting) || (visible && wtoken.waitingForReplacement())) {
boolean changed = false;
boolean runningAppAnimation = false;
if (transit != AppTransition.TRANSIT_UNSET) {
if (wtoken.mAppAnimator.animation == AppWindowAnimator.sDummyAnimation) {
wtoken.mAppAnimator.setNullAnimation();
}
if (applyAnimationLocked(wtoken, lp, transit, visible, isVoiceInteraction)) {
delayed = runningAppAnimation = true;
}

}

}
2.2普通窗口动画
当不能设置transition动画时就会设置普通的窗口动画,然而解锁后的窗口进入动画和普通的窗口动画不太一样,那么一定是在哪里完成了动画的替换
前面有提到,WMS在接受到flag之后会把参数进一步传递给WindowAnimtor对象,这个WindowAnimator是WMS服务中管理窗口动画播放的类,主要函数是updateWindowsLocked(),这个函数比较长,我们分段分析一下
第一步,获取当前的窗口列表并根据传过来的flag计算参数,拿到的窗口列表是按Z轴排序过的,锁屏窗口在上方,其余窗口在下方

    final WindowList windows = mService.getWindowListLocked(displayId);final boolean keyguardGoingAwayToShade =(mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_TO_SHADE) != 0;final boolean keyguardGoingAwayNoAnimation =(mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS) != 0;final boolean keyguardGoingAwayWithWallpaper =(mKeyguardGoingAwayFlags & KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER) != 0;

第二步,在窗口动画播放开始前遍历窗口列表找到需要需要替换解锁动画的窗口,这里使用了一个常量SET_FORCE_HIDING_CHANGED,通过位运算的方式区分锁屏以及锁屏上下方的窗口,需要替换的窗口放在unForceHiding里面,通常大小为1

for (int i = windows.size() - 1; i >= 0; i–) {
WindowState win = windows.get(i);
WindowStateAnimator winAnimator = win.mWinAnimator;
final int flags = win.mAttrs.flags;
boolean canBeForceHidden = mPolicy.canBeForceHidden(win, win.mAttrs);
boolean shouldBeForceHidden = shouldForceHide(win);
if (winAnimator.hasSurface()) {
final boolean wasAnimating = winAnimator.mWasAnimating;
final boolean nowAnimating = winAnimator.stepAnimationLocked(mCurrentTime);
winAnimator.mWasAnimating = nowAnimating;
orAnimating(nowAnimating);

            if (wasAnimating && !winAnimator.mAnimating&& wallpaperController.isWallpaperTarget(win)) {mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;setPendingLayoutChanges(Display.DEFAULT_DISPLAY,WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);}if (mPolicy.isForceHiding(win.mAttrs)) {if (!wasAnimating && nowAnimating) {// 窗口还没有开始播放 mBulkUpdateParams |= SET_FORCE_HIDING_CHANGED;setPendingLayoutChanges(displayId,WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);mService.mFocusMayChange = true;} else if (mKeyguardGoingAway && !nowAnimating) {// Timeout!!Slog.e(TAG, "Timeout waiting for animation to startup");mPolicy.startKeyguardExitAnimation(0, 0);mKeyguardGoingAway = false;}if (win.isReadyForDisplay()) {if (nowAnimating && win.mWinAnimator.mKeyguardGoingAwayAnimation) {mForceHiding = KEYGUARD_ANIMATING_OUT;} else {mForceHiding = win.isDrawnLw() ? KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN;}}} else if (canBeForceHidden) {if (shouldBeForceHidden) {if (!win.hideLw(false, false)) {continue;}} else {boolean applyExistingExitAnimation = mPostKeyguardExitAnimation != null&& !mPostKeyguardExitAnimation.hasEnded()&& !winAnimator.mKeyguardGoingAwayAnimation&& win.hasDrawnLw()&& win.mAttachedWindow == null&& !win.mIsImWindow&& displayId == Display.DEFAULT_DISPLAY;// If the window is already showing and we don't need to apply an existing// Keyguard exit animation, skip.if (!win.showLw(false, false) && !applyExistingExitAnimation) {continue;}final boolean visibleNow = win.isVisibleNow();if (!visibleNow) {// Couldn't really show, must showLw() again when win becomes visible.win.hideLw(false, false);continue;}if ((mBulkUpdateParams & SET_FORCE_HIDING_CHANGED) != 0&& win.mAttachedWindow == null) {if (unForceHiding == null) {unForceHiding = new ArrayList<>();}unForceHiding.add(winAnimator);if ((flags & FLAG_SHOW_WALLPAPER) != 0) {wallpaperInUnForceHiding = true;}if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {startingInUnForceHiding = true;}} else if (applyExistingExitAnimation) {// We're already in the middle of an animation. Use the existing// animation to bring in this window.if (DEBUG_KEYGUARD) Slog.v(TAG,"Applying existing Keyguard exit animation to new window: win="+ win);Animation a = mPolicy.createForceHideEnterAnimation(false,keyguardGoingAwayToShade);winAnimator.setAnimation(a, mPostKeyguardExitAnimation.getStartTime(),STACK_CLIP_BEFORE_ANIM);winAnimator.mKeyguardGoingAwayAnimation = true;winAnimator.mKeyguardGoingAwayWithWallpaper= keyguardGoingAwayWithWallpaper;}final WindowState currentFocus = mService.mCurrentFocus;if (currentFocus == null || currentFocus.mLayer < win.mLayer) {// We are showing on top of the current// focus, so re-evaluate focus to make// sure it is correct.mService.mFocusMayChange = true;}}if ((flags & FLAG_SHOW_WALLPAPER) != 0) {mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE;setPendingLayoutChanges(Display.DEFAULT_DISPLAY,WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER);if (DEBUG_LAYOUT_REPEATS) {mWindowPlacerLocked.debugLayoutRepeats("updateWindowsAndWallpaperLocked 4",getPendingLayoutChanges(Display.DEFAULT_DISPLAY));}}}}// If the window doesn't have a surface, the only thing we care about is the correct// policy visibility.else if (canBeForceHidden) {if (shouldBeForceHidden) {win.hideLw(false, false);} else {win.showLw(false, false);}}final AppWindowToken atoken = win.mAppToken;if (winAnimator.mDrawState == READY_TO_SHOW) {if (atoken == null || atoken.allDrawn) {if (winAnimator.performShowLocked()) {setPendingLayoutChanges(displayId,WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);if (DEBUG_LAYOUT_REPEATS) {mWindowPlacerLocked.debugLayoutRepeats("updateWindowsAndWallpaperLocked 5",getPendingLayoutChanges(displayId));}}}}final AppWindowAnimator appAnimator = winAnimator.mAppAnimator;if (appAnimator != null && appAnimator.thumbnail != null) {if (appAnimator.thumbnailTransactionSeq != mAnimTransactionSequence) {appAnimator.thumbnailTransactionSeq = mAnimTransactionSequence;appAnimator.thumbnailLayer = 0;}if (appAnimator.thumbnailLayer < winAnimator.mAnimLayer) {appAnimator.thumbnailLayer = winAnimator.mAnimLayer;}}if (win.mIsWallpaper) {wallpaper = win;}}

第三步,替换动画,遍历unForceHiding,用解锁动画替换掉原来的窗口动画

if (unForceHiding != null) {
if (!keyguardGoingAwayNoAnimation) {
boolean first = true;
for (int i=unForceHiding.size()-1; i>=0; i–) {
final WindowStateAnimator winAnimator = unForceHiding.get(i);
Animation a = mPolicy.createForceHideEnterAnimation(
wallpaperInUnForceHiding && !startingInUnForceHiding,
keyguardGoingAwayToShade);
if (a != null) {
if (DEBUG_KEYGUARD) Slog.v(TAG,
"Starting keyguard exit animation on window " + winAnimator.mWin);
winAnimator.setAnimation(a, STACK_CLIP_BEFORE_ANIM);
winAnimator.mKeyguardGoingAwayAnimation = true;
winAnimator.mKeyguardGoingAwayWithWallpaper
= keyguardGoingAwayWithWallpaper;
if (first) {
mPostKeyguardExitAnimation = a;
mPostKeyguardExitAnimation.setStartTime(mCurrentTime);
first = false;
}
}
}
} else if (mKeyguardGoingAway) {
mPolicy.startKeyguardExitAnimation(mCurrentTime, 0 /* duration */);
mKeyguardGoingAway = false;
}

        // Wallpaper is going away in un-force-hide motion, animate it as well.if (!wallpaperInUnForceHiding && wallpaper != null && !keyguardGoingAwayNoAnimation) {if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: wallpaper animating away");Animation a = mPolicy.createForceHideWallpaperExitAnimation(keyguardGoingAwayToShade);if (a != null) {wallpaper.mWinAnimator.setAnimation(a);}}}

当然,里面最主要的还是调用PhoneWindowManager的createForceHideEnterAnimation函数来拿到解锁动画对象

public Animation createForceHideEnterAnimation(boolean onWallpaper,
boolean goingToNotificationShade) {
if (goingToNotificationShade) {
return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_behind_enter_fade_in);
}

    AnimationSet set = (AnimationSet) AnimationUtils.loadAnimation(mContext, onWallpaper ?R.anim.lock_screen_behind_enter_wallpaper :R.anim.lock_screen_behind_enter);// TODO: Use XML interpolators when we have log interpolators available in XML.final List<Animation> animations = set.getAnimations();for (int i = animations.size() - 1; i >= 0; --i) {animations.get(i).setInterpolator(mLogDecelerateInterpolator);}return set;
}

对于透明的窗口(launcher)和一般应用的窗口动画是不一样的,动画资源定义在frameworks/base

AppTransition相关推荐

  1. Android11 WMS 之 AppTransition

    目录 一.AppTransition过渡类型 二.AppTransition设置过渡类型调用栈信息 * 新活动中的窗口正在同一任务中的现有窗口之上打开.    int TRANSIT_ACTIVITY ...

  2. android 设置activity启动退出动画 | 解决设置activity 动画不生效问题

    前提: 如果有动画,最好把activity 设置成透明,不然会有黑屏! <style name="Trancelust" parent="AppTheme" ...

  3. weblogic11g集群配置

    最近学习weblogic11g的集群配置,由于以前配置时总是遇到问题,所以现在写下来供大家交流一下.如果不对的地方,希望指教.本文针对与windows xp 32位系统. 有图为证: [1]先把web ...

  4. 图解Android - Android GUI 系统 (2) - 窗口管理 (View, Canvas, Window Manager)

    Android 的窗口管理系统 (View, Canvas, WindowManager) 在图解Android - Zygote 和 System Server 启动分析一 文里,我们已经知道And ...

  5. android窗口动画体系,Android 7.1 GUI系统-窗口管理WMS-动画的执行(七)

    前面只是动画资源的加载过程,下面看下动画是怎么执行起来的? 前面在分析窗口申请的过程中,分析过relayoutWindow中的调用performSurfacePlacement,在这个函数的最后调用了 ...

  6. 《深入理解Android2》读书笔记(五)

    接上篇<深入理解Android2>读书笔记(四) startActivity Am void run() throws RemoteException {try {printMessage ...

  7. 安卓动画系统 - 安卓R

    重要类介绍 Animation Animation定义在frameworks/base/core/java/android/view/animation/Animation.java,其子类有Tran ...

  8. Activity启动过程源码分析

    老罗的Android系统源码分析讲的很不错,网上有很不同层面多源码分析.了解细节,还是自己看源码最直接.个人并没有透彻的研究过Android系统,这一系列的博客就当是读Android源码笔记了.有不对 ...

  9. Android6.0 WMS(八) 显示Activity的启动窗口

     在Android系统中,Activity组件在启动之后,并且在它的窗口显示出来之前,可以显示一个启动窗口.这个启动窗口可以看作是Activity组件的预览窗口,是由WindowManagerSe ...

最新文章

  1. EMC:欲占企业数据中心市场大半江山
  2. 7216:Minecraft
  3. 如何用模型分析中国经济?
  4. java aspectj_初窥AspectJ
  5. 并发查询parallel_惯用并发:flatMap()与parallel()– RxJava常见问题解答
  6. 看阿里云如何为直播用户营造临场沉浸感?
  7. 让WebStorm支持dojo的智能提示
  8. 汇编语言定时器转化为c语言,不用定时器和汇编语言,只用C语言实现精确无误的延时...
  9. ssas对数据仓库_SSAS中的多对多关系简介
  10. 基于Python SimpleHTTPServer.py的修改脚本:HTTP文件服务器,修正中文目录列表,支持视频文件在线播放
  11. mac下vscode代码格式化及其他常用快捷键
  12. ZETag云标签是什么?如何实现贵重包裹跟踪、供应链数字化
  13. pr调整图层java,glm`prepected()`错误:没有适用于'predict'的方法应用于类“list”的对象...
  14. 如何在PPT中插入Visio文件
  15. Power BI可视化技巧:正负值配色
  16. GitBook在Windows下安装部署
  17. 【Hydro】龙格-库塔方法的公式推导
  18. 85后北大毕业生放弃年薪百万工作,开发制作微信“清粉”软件牟利:涉案800万...
  19. 【MOMO】高水平期刊目录(持续更新)
  20. [英语学习]主动语态变被动语态

热门文章

  1. from后面跟select语句原来是这个意思
  2. ERROR: NMO not setuid-root (Unix-only)
  3. java实现excel导入导出(jxl),Java入门你值得拥有
  4. 百度地图 驾车导航用来生成路线 轨迹回放(LuShu)
  5. phpnow怎么改php版本,PHPnow升级PHP版本的方法。
  6. Android流量监控论文,Android平台流量监控软件的设计与实现
  7. C# 将Base64转换为图片并保存到Image数组
  8. 虚拟机安装MySQL
  9. 万物新生(爱回收)三季度GMV与收入均超预期达成 总营收同比增长48%
  10. Win11桌面图标的小盾牌怎么去掉?