解鎖的framework流程與解鎖后的加載動畫

前面的文章說過,AppTransition代表了activity組件的切換過程跃洛,其實(shí)解鎖也是一種AppTrasition,只不過需要隱藏的activity組件是鎖屏窗口罷了(嚴(yán)格來說并不是鎖屏窗口被隱藏了玛追,它只是調(diào)整了窗口的大小及相關(guān)參數(shù)税课,最后變成狀態(tài)欄),解鎖過程的窗口變化主要是以下幾點(diǎn)

  • 改變鎖屏窗口狀態(tài)
  • 顯示鎖屏下方的窗口
  • 播放解鎖動畫

1.解鎖過程的appTransition

解鎖過程的核心實(shí)質(zhì)上是鎖屏啟動了一個(gè)runnable痊剖,通知AMS和WMS顯示鎖屏下方的activity組件窗口以及調(diào)用該activity組件的生命周期韩玩,向AMS和WMS發(fā)送命令的時(shí)候會傳遞一些flag,這些flag和解鎖的場景有關(guān)陆馁,一般來說我們只用關(guān)注第一個(gè)找颓,即WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS,例如熄屏的時(shí)候使用指紋解鎖亮屏叮贩,這期間不需要顯示解鎖動畫击狮,于是就需要傳遞這個(gè)消息給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的具體實(shí)現(xiàn)

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();  // 執(zhí)行apptransition

                    // Some stack visibility might change (e.g. docked stack)
                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); //apptransition解鎖后調(diào)整activitystack
                    applyVrModeIfNeededLocked(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發(fā)送消息,WMS接收到消息后就會把flag傳遞給負(fù)責(zé)管理窗口動畫的WindowAnimator對象mAnimator益老,同時(shí)刷新系統(tǒng)UI

2.解鎖過程的窗口動畫

AppTransition的過程前面的文章有說彪蓬,這里主要關(guān)注解鎖過程的窗口動畫

2.1activity組件切換動畫

設(shè)置activity組件的切換動畫會調(diào)到如下函數(shù),其中,wtoken.hidden表示activity組件是否應(yīng)該處于hidden狀態(tài)捺萌,然而對于解鎖后需要打開的activity組件來說档冬,hidden = false,而visibility = true,所以實(shí)際上解鎖過程是不會設(shè)置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普通窗口動畫

當(dāng)不能設(shè)置transition動畫時(shí)就會設(shè)置普通的窗口動畫酷誓,然而解鎖后的窗口進(jìn)入動畫和普通的窗口動畫不太一樣披坏,那么一定是在哪里完成了動畫的替換
前面有提到,WMS在接受到flag之后會把參數(shù)進(jìn)一步傳遞給WindowAnimtor對象盐数,這個(gè)WindowAnimator是WMS服務(wù)中管理窗口動畫播放的類棒拂,主要函數(shù)是updateWindowsLocked(),這個(gè)函數(shù)比較長玫氢,我們分段分析一下
第一步帚屉,獲取當(dāng)前的窗口列表并根據(jù)傳過來的flag計(jì)算參數(shù),拿到的窗口列表是按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;

第二步猜绣,在窗口動畫播放開始前遍歷窗口列表找到需要需要替換解鎖動畫的窗口,這里使用了一個(gè)常量SET_FORCE_HIDING_CHANGED灰殴,通過位運(yùn)算的方式區(qū)分鎖屏以及鎖屏上下方的窗口,需要替換的窗口放在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);
                }
            }
        }

當(dāng)然掰伸,里面最主要的還是調(diào)用PhoneWindowManager的createForceHideEnterAnimation函數(shù)來拿到解鎖動畫對象

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)和一般應(yīng)用的窗口動畫是不一樣的,動畫資源定義在frameworks/base

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末怀估,一起剝皮案震驚了整個(gè)濱河市狮鸭,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌多搀,老刑警劉巖歧蕉,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異康铭,居然都是意外死亡惯退,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門从藤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來催跪,“玉大人,你說我怎么就攤上這事夷野“谜簦” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵悯搔,是天一觀的道長骑丸。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么者娱? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任抡笼,我火速辦了婚禮,結(jié)果婚禮上黄鳍,老公的妹妹穿的比我還像新娘推姻。我一直安慰自己,他們只是感情好框沟,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布藏古。 她就那樣靜靜地躺著,像睡著了一般忍燥。 火紅的嫁衣襯著肌膚如雪拧晕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天梅垄,我揣著相機(jī)與錄音厂捞,去河邊找鬼。 笑死队丝,一個(gè)胖子當(dāng)著我的面吹牛靡馁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播机久,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼臭墨,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了膘盖?” 一聲冷哼從身側(cè)響起胧弛,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎侠畔,沒想到半個(gè)月后结缚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡践图,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年掺冠,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片码党。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡德崭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出揖盘,到底是詐尸還是另有隱情眉厨,我是刑警寧澤,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布兽狭,位于F島的核電站憾股,受9級特大地震影響鹿蜀,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜服球,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一茴恰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧斩熊,春花似錦往枣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至霸株,卻和暖如春雕沉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背去件。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工坡椒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人箫攀。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓肠牲,卻偏偏與公主長得像,于是被迫代替她去往敵國和親靴跛。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,867評論 25 707
  • 無論是系統(tǒng)中窗口的動畫渡嚣,還是應(yīng)用中某一個(gè)View的動畫梢睛,它們的原理都是一樣的。當(dāng)一個(gè)窗口打開的時(shí)候识椰,為了看起來更緩...
    LooperJing閱讀 7,734評論 5 13
  • Android Studio JNI流程首先在java代碼聲明本地方法 用到native關(guān)鍵字 本地方法不用去實(shí)現(xiàn)...
    MigrationUK閱讀 11,848評論 7 123
  • 累了绝葡,就躺下吧 別管那點(diǎn)灰塵 可被玷污的 早已洗脫不去 什么時(shí)候 絕望 什么時(shí)候 希望 老師沒...
    茅老狗閱讀 224評論 0 0
  • 我自己都不知道自己從什么時(shí)候開始喜歡這個(gè)有點(diǎn)胖,頭發(fā)有點(diǎn)卷兒的中年男人腹鹉。搞起笑來一副鬼馬的樣子藏畅。但是他安靜下來...
    流行歌曲閱讀 870評論 0 2