Android 关闭Activity切换过渡动画
Android 9.0以前关闭过渡动画效果只需要把开发者模式中过渡动画缩放设为0就可以。也就是把def_window_transition_scale改为0%
frameworks/base/packages/SettingsProvider/res/values/defaults.xml+ <fraction name="def_window_transition_scale">100%</fraction>
- <fraction name="def_window_transition_scale">0%</fraction>
9.0以后按这种方式修改后屏幕会闪一下黑屏,整个窗口会抖动一下,感觉动画没有完全关闭。
跟下流程发现还是有些不一样的地方。
void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,boolean newTask, boolean keepCurTransition, ActivityOptions options) {TaskRecord rTask = r.getTask();final int taskId = rTask.taskId;// mLaunchTaskBehind tasks get placed at the back of the task stack.if (!r.mLaunchTaskBehind && (taskForIdLocked(taskId) == null || newTask)) {// Last activity in task had been removed or ActivityManagerService is reusing task.// Insert or replace.// Might not even be in.insertTaskAtTop(rTask, r);}TaskRecord task = null;if (!newTask) {// If starting in an existing task, find where that is...boolean startIt = true;for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {task = mTaskHistory.get(taskNdx);if (task.getTopActivity() == null) {// All activities in task are finishing.continue;}if (task == rTask) {// Here it is! Now, if this is not yet visible to the// user, then just add it without starting; it will// get started when the user navigates back to it.if (!startIt) {if (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to task "+ task, new RuntimeException("here").fillInStackTrace());r.createWindowContainer();ActivityOptions.abort(options);return;}break;} else if (task.numFullscreen > 0) {startIt = false;}}}// Place a new activity at top of stack, so it is next to interact with the user.// If we are not placing the new activity frontmost, we do not want to deliver the// onUserLeaving callback to the actual frontmost activityfinal TaskRecord activityTask = r.getTask();if (task == activityTask && mTaskHistory.indexOf(task) != (mTaskHistory.size() - 1)) {mStackSupervisor.mUserLeaving = false;if (DEBUG_USER_LEAVING) Slog.v(TAG_USER_LEAVING,"startActivity() behind front, mUserLeaving=false");}task = activityTask;// Slot the activity into the history stack and proceedif (DEBUG_ADD_REMOVE) Slog.i(TAG, "Adding activity " + r + " to stack to task " + task,new RuntimeException("here").fillInStackTrace());// TODO: Need to investigate if it is okay for the controller to already be created by the// time we get to this point. I think it is, but need to double check.// Use test in b/34179495 to trace the call path.if (r.getWindowContainerController() == null) {r.createWindowContainer();}task.setFrontOfTask();if (!isHomeOrRecentsStack() || numActivities() > 0) {if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,"Prepare open transition: starting " + r);//app设置FLAG_ACTIVITY_NO_ANIMATION,主动禁用过渡动画if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {mWindowManager.prepareAppTransition(TRANSIT_NONE, keepCurTransition);mStackSupervisor.mNoAnimActivities.add(r);} else {int transit = TRANSIT_ACTIVITY_OPEN;if (newTask) {if (r.mLaunchTaskBehind) {transit = TRANSIT_TASK_OPEN_BEHIND;} else {// If a new task is being launched, then mark the existing top activity as// supporting picture-in-picture while pausing only if the starting activity// would not be considered an overlay on top of the current activity// (eg. not fullscreen, or the assistant)if (canEnterPipOnTaskSwitch(focusedTopActivity,null /* toFrontTask */, r, options)) {focusedTopActivity.supportsEnterPipOnTaskSwitch = true;}transit = TRANSIT_TASK_OPEN;}}//准备过渡动画mWindowManager.prepareAppTransition(transit, keepCurTransition);mStackSupervisor.mNoAnimActivities.remove(r);}boolean doShow = true;if (newTask) {// Even though this activity is starting fresh, we still need// to reset it to make sure we apply affinities to move any// existing activities from other tasks in to it.// If the caller has requested that the target task be// reset, then do so.if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {resetTaskIfNeededLocked(r, r);doShow = topRunningNonDelayedActivityLocked(null) == r;}} else if (options != null && options.getAnimationType()== ActivityOptions.ANIM_SCENE_TRANSITION) {doShow = false;}if (r.mLaunchTaskBehind) {// Don't do a starting window for mLaunchTaskBehind. More importantly make sure we// tell WindowManager that r is visible even though it is at the back of the stack.r.setVisibility(true);ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);} else if (SHOW_APP_STARTING_PREVIEW && doShow) {// Figure out if we are transitioning from another activity that is// "has the same starting icon" as the next one. This allows the// window manager to keep the previous window it had previously// created, if it still had one.TaskRecord prevTask = r.getTask();ActivityRecord prev = prevTask.topRunningActivityWithStartingWindowLocked();if (prev != null) {// We don't want to reuse the previous starting preview if:// (1) The current activity is in a different task.if (prev.getTask() != prevTask) {prev = null;}// (2) The current activity is already displayed.else if (prev.nowVisible) {prev = null;}}r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));}} else {// If this is the first activity, don't do any fancy animations,// because there is nothing for it to animate on top of.ActivityOptions.abort(options);}}
最终通过调用next.setVisibility(true),调用到AppWindowToken中的setVisibility方法中把要切换的activity设为可见状态。
boolean setVisibility(WindowManager.LayoutParams lp,boolean visible, int transit, boolean performLayout, boolean isVoiceInteraction) {boolean delayed = false;inPendingTransaction = false;// Reset the state of mHiddenSetFromTransferredStartingWindow since visibility is actually// been set by the app now.mHiddenSetFromTransferredStartingWindow = false;// Allow for state changes and animation to be applied if:// * token is transitioning visibility state// * or the token was marked as hidden and is exiting before we had a chance to play the// transition animation// * or this is an opening app and windows are being replaced.boolean visibilityChanged = false;if (isHidden() == visible || (isHidden() && mIsExiting) || (visible && waitingForReplacement())) {final AccessibilityController accessibilityController = mService.mAccessibilityController;boolean changed = false;if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM,"Changing app " + this + " hidden=" + isHidden() + " performLayout=" + performLayout);boolean runningAppAnimation = false;if (transit != WindowManager.TRANSIT_UNSET) {//使用动画,9.0之前该方法实现是在WindowManagerService中if (applyAnimationLocked(lp, transit, visible, isVoiceInteraction)) {delayed = runningAppAnimation = true;}final WindowState window = findMainWindow();//TODO (multidisplay): Magnification is supported only for the default display.if (window != null && accessibilityController != null&& getDisplayContent().getDisplayId() == DEFAULT_DISPLAY) {accessibilityController.onAppWindowTransitionLocked(window, transit);}changed = true;}...
看下applyAnimationLocked,有这样一段代码
boolean applyAnimationLocked(WindowManager.LayoutParams lp, int transit, boolean enter,boolean isVoiceInteraction) {//判断是否已禁用TransationAnimationif (mService.mDisableTransitionAnimation || !shouldAnimate(transit)) {if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) {Slog.v(TAG_WM, "applyAnimation: transition animation is disabled or skipped."+ " atoken=" + this);}cancelAnimation();return false;}// Only apply an animation if the display isn't frozen. If it is frozen, there is no reason...
接着看WindowManagerService
WindowManagerService(Context context, InputManagerService inputManager,boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,WindowManagerPolicy policy) {installLock(this, INDEX_WINDOW);mContext = context;mHaveInputMethods = haveInputMethods;mAllowBootMessages = showBootMsgs;mOnlyCore = onlyCore;mLimitedAlphaCompositing = context.getResources().getBoolean(com.android.internal.R.bool.config_sf_limitedAlpha);mHasPermanentDpad = context.getResources().getBoolean(com.android.internal.R.bool.config_hasPermanentDpad);mInTouchMode = context.getResources().getBoolean(com.android.internal.R.bool.config_defaultInTouchMode);mDrawLockTimeoutMillis = context.getResources().getInteger(com.android.internal.R.integer.config_drawLockTimeoutMillis);mAllowAnimationsInLowPowerMode = context.getResources().getBoolean(com.android.internal.R.bool.config_allowAnimationsInLowPowerMode);mMaxUiWidth = context.getResources().getInteger(com.android.internal.R.integer.config_maxUiWidth);//直接读取configmDisableTransitionAnimation = context.getResources().getBoolean(com.android.internal.R.bool.config_disableTransitionAnimation);mInputManager = inputManager; // Must be before createDisplayContentLocked.mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);mDisplaySettings = new DisplaySettings();mDisplaySettings.readSettingsLocked();
最后尝试把config_disableTransitionAnimation改为true,验证过渡动画关闭并且没有闪屏。