Android 14 transtion 动画流程

1.接口

shell入口通过TransitionPlayerImpl与wm core 通信


   @BinderThread
    private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
        @Override
        public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
                SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
                throws RemoteException {
            mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
                    iBinder, transitionInfo, t, finishT));
        }
 
        @Override
        public void requestStartTransition(IBinder iBinder,
                TransitionRequestInfo request) throws RemoteException {
            mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request));
        }
    }
}
分为

1. requestStartTransition

2.onTransitionReady

两个阶段



1.requestStartTransition
wm services

new 一个动画放进mActiveSyncs中



04-23 18:43:25.195 2077 4588 D jinyanmeiainima: BLASTSyncEngine startSyncSet s.mSyncId:6
04-23 18:43:25.195 2077 4588 D jinyanmeiainima: BLASTSyncEngine startSyncSet s:com.android.server.wm.BLASTSyncEngine$SyncGroup@3ee8531
04-23 18:43:25.195 2077 4588 D jinyanmeiainima: BLASTSyncEngine startSyncSet mActiveSyncs++++:0
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: BLASTSyncEngine startSyncSet mActiveSyncs---:1
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: java.lang.RuntimeException: jinyanmeiainima
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.BLASTSyncEngine.startSyncSet(BLASTSyncEngine.java:327)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.BLASTSyncEngine.startSyncSet(BLASTSyncEngine.java:305)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.Transition.startCollecting(Transition.java:422)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.TransitionController.moveToCollecting(TransitionController.java:222)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.TransitionController.moveToCollecting(TransitionController.java:203)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.TransitionController.createTransition(TransitionController.java:197)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.TransitionController.createTransition(TransitionController.java:179)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.ActivityTaskSupervisor.findTaskToMoveToFront(ActivityTaskSupervisor.java:1577)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.ActivityTaskManagerService.moveTaskToFrontLocked(ActivityTaskManagerService.java:2497)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.ActivityTaskSupervisor.startActivityFromRecents(ActivityTaskSupervisor.java:3034)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.ActivityTaskManagerService.startActivityFromRecents(ActivityTaskManagerService.java:1918)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at android.app.IActivityTaskManager$Stub.onTransact(IActivityTaskManager.java:1434)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at com.android.server.wm.ActivityTaskManagerService.onTransact(ActivityTaskManagerService.java:5840)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at android.os.Binder.execTransactInternal(Binder.java:1348)
04-23 18:43:25.196 2077 4588 D jinyanmeiainima: at android.os.Binder.execTransact(Binder.java:1279)

new出来后调用task.mTransitionController.collect(task);

04-24 16:06:47.682 1815 2160 D jinyanmeiani: ChangeInfo 111111111 this:Task{fa5361 #69 type=standard A=10143:comgallery.home}
04-24 16:06:47.682 1815 2160 D jinyanmeiani: ChangeInfo 111111111 mVisible:true
04-24 16:06:47.682 1815 2160 D jinyanmeiani: ChangeInfo 111111111 mWindowingMode:5
04-24 16:06:47.682 1815 2160 D jinyanmeiani: java.lang.RuntimeException: jinyanmeiani
04-24 16:06:47.682 1815 2160 D jinyanmeiani: at com.android.server.wm.Transition$ChangeInfo.<init>(Transition.java:2258)
04-24 16:06:47.682 1815 2160 D jinyanmeiani: at com.android.server.wm.Transition.collect(Transition.java:492)
04-24 16:06:47.682 1815 2160 D jinyanmeiani: at com.android.server.wm.TransitionController.collect(TransitionController.java:568)
04-24 16:06:47.682 1815 2160 D jinyanmeiani: at com.android.server.wm.ActivityTaskSupervisor.findTaskToMoveToFront(ActivityTaskSupervisor.java:1529)
04-24 16:06:47.682 1815 2160 D jinyanmeiani: at com.android.server.wm.ActivityTaskManagerService.moveTaskToFrontLocked(ActivityTaskManagerService.java:2464)
04-24 16:06:47.682 1815 2160 D jinyanmeiani: at com.android.server.wm.ActivityTaskSupervisor.startActivityFromRecents(ActivityTaskSupervisor.java:2901)
04-24 16:06:47.682 1815 2160 D jinyanmeiani: at com.android.server.wm.ActivityTaskManagerService.startActivityFromRecents(ActivityTaskManagerService.java:1931)
04-24 16:06:47.682 1815 2160 D jinyanmeiani: at android.app.IActivityTaskManager$Stub.onTransact(IActivityTaskManager.java:1430)
04-24 16:06:47.682 1815 2160 D jinyanmeiani: at com.android.server.wm.ActivityTaskManagerService.onTransact(ActivityTaskManagerService.java:5786)
04-24 16:06:47.682 1815 2160 D jinyanmeiani: at android.os.Binder.execTransactInternal(Binder.java:1348)
04-24 16:06:47.682 1815 2160 D jinyanmeiani: at android.os.Binder.execTransact(Binder.java:1279)
/** @see Transition#collect */
void collect(@NonNull WindowContainer wc) {
    if (mCollectingTransition == null) return;
    mCollectingTransition.collect(wc);
}

计算所有的到mChanges

根据WindowContainer new 一个ChangeInfo

ChangeInfo(@NonNull WindowContainer origState) {
    mContainer = origState;
    mVisible = origState.isVisibleRequested();
    mWindowingMode = origState.getWindowingMode();
    mAbsoluteBounds.set(origState.getBounds());
    mShowWallpaper = origState.showWallpaper();
    mRotation = origState.getWindowConfiguration().getRotation();
    mStartParent = origState.getParent();
}

把changeinfo放入mChanges

mChanges是一个map

key:WindowContainer

value:由key:WindowContainer构成的ChangeInfo

此列表在开始做动画时候会判断里面的key,value是否满足做动画的条件

void collect(@NonNull WindowContainer wc) {
      if (mState < STATE_COLLECTING) {
          throw new IllegalStateException("Transition hasn't started collecting.");
      }
      if (!isCollecting()) {
          // Too late, transition already started playing, so don't collect.
          return;
      }
      ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Collecting in transition %d: %s",
              mSyncId, wc);
      // "snapshot" all parents (as potential promotion targets). Do this before checking
      // if this is already a participant in case it has since been re-parented.
      for (WindowContainer<?> curr = getAnimatableParent(wc);
              curr != null && !mChanges.containsKey(curr);
              curr = getAnimatableParent(curr)) {
          mChanges.put(curr, new ChangeInfo(curr)); // 放入队列中
          if (isReadyGroup(curr)) {
              mReadyTracker.addGroup(curr);
              ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, " Creating Ready-group for"
                              + " Transition %d with root=%s", mSyncId, curr);
          }
      }
      if (mParticipants.contains(wc)) return;
      // Wallpaper is like in a static drawn state unless display may have changes, so exclude
      // the case to reduce transition latency waiting for the unchanged wallpaper to redraw.
      final boolean needSyncDraw = !isWallpaper(wc) || mParticipants.contains(wc.mDisplayContent);
      if (needSyncDraw) {
          mSyncEngine.addToSyncSet(mSyncId, wc);
      }
      ChangeInfo info = mChanges.get(wc);
      if (info == null) {
          info = new ChangeInfo(wc);
          Slog.d("jinyanmeiainima","collect                  wc:" + wc );
          Slog.d("jinyanmeiainima","collect             info:" + info );
 
          Slog.d("jinyanmeiainima","collect mChanges.size()+++:" + mChanges.size() );
          mChanges.put(wc, info);
          Slog.d("jinyanmeiainima","collect mChanges.size()---:" + mChanges.size() );
 
      }
      mParticipants.add(wc);
      if (wc.getDisplayContent() != null && !mTargetDisplays.contains(wc.getDisplayContent())) {
          mTargetDisplays.add(wc.getDisplayContent());
      }
      if (info.mShowWallpaper) {
          // Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
          final WindowState wallpaper =
                  wc.getDisplayContent().mWallpaperController.getTopVisibleWallpaper();
          if (wallpaper != null) {
              collect(wallpaper.mToken);
          }
      }
  }
把new出来的动画申请开始动画requestStartTransition

04-23 18:43:25.211 2077 4588 D jinyanmeiainima: requestStartTransition displayChange:
04-23 18:43:25.211 2077 4588 D jinyanmeiainima: java.lang.RuntimeException: jinyanmeiainima
04-23 18:43:25.211 2077 4588 D jinyanmeiainima: at com.android.server.wm.TransitionController.requestStartTransition(TransitionController.java:548)
04-23 18:43:25.211 2077 4588 D jinyanmeiainima: at com.android.server.wm.ActivityTaskSupervisor.findTaskToMoveToFront(ActivityTaskSupervisor.java:1622)
04-23 18:43:25.211 2077 4588 D jinyanmeiainima: at com.android.server.wm.ActivityTaskManagerService.moveTaskToFrontLocked(ActivityTaskManagerService.java:2497)
04-23 18:43:25.211 2077 4588 D jinyanmeiainima: at com.android.server.wm.ActivityTaskSupervisor.startActivityFromRecents(ActivityTaskSupervisor.java:3034)
04-23 18:43:25.211 2077 4588 D jinyanmeiainima: at com.android.server.wm.ActivityTaskManagerService.startActivityFromRecents(ActivityTaskManagerService.java:1918)
04-23 18:43:25.211 2077 4588 D jinyanmeiainima: at android.app.IActivityTaskManager$Stub.onTransact(IActivityTaskManager.java:1434)
04-23 18:43:25.211 2077 4588 D jinyanmeiainima: at com.android.server.wm.ActivityTaskManagerService.onTransact(ActivityTaskManagerService.java:5840)
04-23 18:43:25.211 2077 4588 D jinyanmeiainima: at android.os.Binder.execTransactInternal(Binder.java:1348)
04-23 18:43:25.211 2077 4588 D jinyanmeiainima: at android.os.Binder.execTransact(Binder.java:1279)
Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask,
        @Nullable RemoteTransition remoteTransition,
        @Nullable TransitionRequestInfo.DisplayChange displayChange) {
    Slog.d("jinyanmeiainima","requestStartTransition transition:" + transition );
    Slog.d("jinyanmeiainima","requestStartTransition startTask:" + startTask );
    Slog.d("jinyanmeiainima","requestStartTransition remoteTransition:" + remoteTransition );
    Slog.d("jinyanmeiainima","requestStartTransition displayChange:" + displayChange );
 
    if (mIsWaitingForDisplayEnabled) {
        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "Disabling player for transition"
                + " #%d because display isn't enabled yet", transition.getSyncId());
        transition.mIsPlayerEnabled = false;
        transition.mLogger.mRequestTimeNs = SystemClock.uptimeNanos();
        mAtm.mH.post(() -> mAtm.mWindowOrganizerController.startTransition(
                transition.getToken(), null));
        return transition;
    }
    if (mTransitionPlayer == null || transition.isAborted()) {
        // Apparently, some tests will kill(and restart) systemui, so there is a chance that
        // the player might be transiently null.
        if (transition.isCollecting()) {
            transition.abort();
        }
        return transition;
    }
    try {
        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                "Requesting StartTransition: %s", transition);
        ActivityManager.RunningTaskInfo info = null;
        if (startTask != null) {
            info = new ActivityManager.RunningTaskInfo();
            startTask.fillTaskInfo(info);
        }
        final TransitionRequestInfo request = new TransitionRequestInfo(
                transition.mType, info, remoteTransition, displayChange);
        transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
        transition.mLogger.mRequest = request;
        Slog.d("jinyanmeiainima","requestStartTransition transition.getToken():" + transition.getToken() );
        Slog.d("jinyanmeiainima","requestStartTransition               request:" + request);
 
        Slog.d("jinyanmeiainima","requestStartTransition displayChange:" ,  new RuntimeException("jinyanmeiainima") );
        mTransitionPlayer.requestStartTransition(transition.getToken(), request);
        transition.setRemoteTransition(remoteTransition);
    } catch (RemoteException e) {
        Slog.e(TAG, "Error requesting transition", e);
        transition.start();
    }
    return transition;
}

转到shell:

Transition requestStartTransition(@NonNull Transition transition, @Nullable Task startTask,

requestStartTransition
            mTransitionPlayer.requestStartTransition(transition.getToken(), request);
            transition.setRemoteTransition(remoteTransition);

shell

   @BinderThread
    private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
        @Override
        public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
                SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
                throws RemoteException {
            mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
                    iBinder, transitionInfo, t, finishT));
        }
 
        @Override
        public void requestStartTransition(IBinder iBinder,
                TransitionRequestInfo request) throws RemoteException {
            mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request));
        }
    }
}
void requestStartTransition(@NonNull IBinder transitionToken,
        @Nullable TransitionRequestInfo request) {
    ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s",
            transitionToken, request);
    if (isTransitionKnown(transitionToken)) {
        throw new RuntimeException("Transition already started " + transitionToken);
    }
    final ActiveTransition active = new ActiveTransition();
    WindowContainerTransaction wct = null;
 
    // If we have sleep, we use a special handler and we try to finish everything ASAP.
    if (request.getType() == TRANSIT_SLEEP) {
        mSleepHandler.handleRequest(transitionToken, request);
        active.mHandler = mSleepHandler;
    } else {
        for (int i = mHandlers.size() - 1; i >= 0; --i) {
            wct = mHandlers.get(i).handleRequest(transitionToken, request);
            if (wct != null) {
                active.mHandler = mHandlers.get(i);
                break;
            }
        }
        if (request.getDisplayChange() != null) {
            TransitionRequestInfo.DisplayChange change = request.getDisplayChange();
            if (change.getEndRotation() != change.getStartRotation()) {
                // Is a rotation, so dispatch to all displayChange listeners
                if (wct == null) {
                    wct = new WindowContainerTransaction();
                }
                mDisplayController.getChangeController().dispatchOnDisplayChange(wct,
                        change.getDisplayId(), change.getStartRotation(),
                        change.getEndRotation(), null /* newDisplayAreaInfo */);
            }
        }
    }
    mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct);
    active.mToken = transitionToken;
    // Currently, WMCore only does one transition at a time. If it makes a requestStart, it
    // is already collecting that transition on core-side, so it will be the next one to
    // become ready. There may already be pending transitions added as part of direct
    // `startNewTransition` but if we have a request now, it means WM created the request
    // transition before it acknowledged any of the pending `startNew` transitions. So, insert
    // it at the front.
    mPendingTransitions.add(0, active);  // 添加到mPendingTransitions
}
private static final class ActiveTransition {
    IBinder mToken;
    TransitionHandler mHandler;
    boolean mAborted;
    TransitionInfo mInfo;
    SurfaceControl.Transaction mStartT;
    SurfaceControl.Transaction mFinishT;
 
    /** Ordered list of transitions which have been merged into this one. */
    private ArrayList<ActiveTransition> mMerged;
}
转到wm core

04-23 18:43:22.342 2980 3221 D jinyanmeiainima: WindowOrganizer transitionToken transitionToken:android.os.BinderProxy@3268f3
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: WindowOrganizer startTransition t:null
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: java.lang.RuntimeException: jinyanmeiainima
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: at android.window.WindowOrganizer.startTransition(WindowOrganizer.java:112)
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: at com.android.wm.shell.transition.Transitions.requestStartTransition(Transitions.java:891)
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl.lambda$requestStartTransition$1(Transitions.java:1129)
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl.$r8$lambda$FkH1M0vUh3zDx8R5iMruInPEXLI(Unknown Source:0)
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl$$ExternalSyntheticLambda1.run(Unknown Source:6)
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: at android.os.Handler.handleCallback(Handler.java:958)
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: at android.os.Handler.dispatchMessage(Handler.java:99)
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: at android.os.Looper.loopOnce(Looper.java:216)
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: at android.os.Looper.loop(Looper.java:305)
04-23 18:43:22.343 2980 3221 D jinyanmeiainima: at android.os.HandlerThread.run(HandlerThread.java:67)
 WindowOrganizer.java
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
   public void startTransition(@NonNull IBinder transitionToken,
           @Nullable WindowContainerTransaction t) {
       try {
           getWindowOrganizerController().startTransition(transitionToken, t);
       } catch (RemoteException e) {
           throw e.rethrowFromSystemServer();
       }
   }
转到 wm services
WindowOrganizerController
WindowOrganizerController.java   
private IBinder startTransition(@WindowManager.TransitionType int type,
            @Nullable IBinder transitionToken, @Nullable WindowContainerTransaction t) {
        enforceTaskPermission("startTransition()");
        final CallerInfo caller = new CallerInfo();
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mGlobalLock) {
                Transition transition = Transition.fromBinder(transitionToken);
                if (mTransitionController.getTransitionPlayer() == null && transition == null) {
                    Slog.w(TAG, "Using shell transitions API for legacy transitions.");
                    if (t == null) {
                        throw new IllegalArgumentException("Can't use legacy transitions in"
                                + " compatibility mode with no WCT.");
                    }
                    applyTransaction(t, -1 /* syncId */, null, caller);
                    return null;
                }
                // In cases where transition is already provided, the "readiness lifecycle" of the
                // transition is determined outside of this transaction. However, if this is a
                // direct call from shell, the entire transition lifecycle is contained in the
                // provided transaction and thus we can setReady immediately after apply.
                final boolean needsSetReady = transition == null && t != null;
                final WindowContainerTransaction wct =
                        t != null ? t : new WindowContainerTransaction();
                if (transition == null) {
                    if (type < 0) {
                        throw new IllegalArgumentException("Can't create transition with no type");
                    }
                    // If there is already a collecting transition, queue up a new transition and
                    // return that. The actual start and apply will then be deferred until that
                    // transition starts collecting. This should almost never happen except during
                    // tests.
                    if (mService.mWindowManager.mSyncEngine.hasActiveSync()) {
                        Slog.w(TAG, "startTransition() while one is already collecting.");
                        final Transition nextTransition = new Transition(type, 0 /* flags */,
                                mTransitionController, mService.mWindowManager.mSyncEngine);
                        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                                "Creating Pending Transition: %s", nextTransition);
                        mService.mWindowManager.mSyncEngine.queueSyncSet(
                                // Make sure to collect immediately to prevent another transition
                                // from sneaking in before it. Note: moveToCollecting internally
                                // calls startSyncSet.
                                () -> mTransitionController.moveToCollecting(nextTransition),
                                () -> {
                                    nextTransition.start();
                                    applyTransaction(wct, -1 /*syncId*/, nextTransition, caller);
                                    if (needsSetReady) {
                                        nextTransition.setAllReady();
                                    }
                                });
                        return nextTransition.getToken();
                    }
                    transition = mTransitionController.createTransition(type);
                }
                if (!transition.isCollecting() && !transition.isForcePlaying()) {
                    Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
                            + " means Shell took too long to respond to a request. WM State may be"
                            + " incorrect now, please file a bug");
                    applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller);
                    return transition.getToken();
                }
                transition.start();  /到这里
                transition.mLogger.mStartWCT = wct;
                applyTransaction(wct, -1 /*syncId*/, transition, caller);
                if (needsSetReady) {
                    transition.setAllReady();
                }
                return transition.getToken();
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }

04-23 18:44:02.186 2107 2323 D jinyanmeiainima: BLASTSyncEngine setReady ready:true
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: java.lang.RuntimeException: setReady
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: at com.android.server.wm.BLASTSyncEngine$SyncGroup.setReady(BLASTSyncEngine.java:240)
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: at com.android.server.wm.BLASTSyncEngine$SyncGroup.-$$Nest$msetReady(Unknown Source:0)
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: at com.android.server.wm.BLASTSyncEngine.setReady(BLASTSyncEngine.java:356)
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: at com.android.server.wm.Transition.applyReady(Transition.java:657)
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: at com.android.server.wm.Transition.start(Transition.java:446)
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: at com.android.server.wm.WindowOrganizerController.startTransition(WindowOrganizerController.java:335)
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: at com.android.server.wm.WindowOrganizerController.startTransition(WindowOrganizerController.java:267)
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: at android.window.IWindowOrganizerController$Stub.onTransact(IWindowOrganizerController.java:264)
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: at com.android.server.wm.WindowOrganizerController.onTransact(WindowOrganizerController.java:185)
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: at android.os.Binder.execTransactInternal(Binder.java:1343)
04-23 18:44:02.186 2107 2323 D jinyanmeiainima: at android.os.Binder.execTransact(Binder.java:1279)

然后调用到BLASTSyncEngine setReady 将动画状态置为ready

开始刷新

  BLASTSyncEngine.java     
private void setReady(boolean ready) {
 
            if (mReady == ready) {
                return;
            }
            Slog.d("jinyanmeiainima","BLASTSyncEngine setReady              ready:" + ready, new RuntimeException("setReady"));
 
            ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Set ready", mSyncId);
            mReady = ready;
            if (!ready) return;
            mWm.mWindowPlacerLocked.requestTraversal();
        }
private static final class ActiveTransition {
    IBinder mToken;
    TransitionHandler mHandler;
    boolean mAborted;
    TransitionInfo mInfo;
    SurfaceControl.Transaction mStartT;
    SurfaceControl.Transaction mFinishT;
 
    /** Ordered list of transitions which have been merged into this one. */
    private ArrayList<ActiveTransition> mMerged;
}

onTransactionReady阶段

2.1 数据准备,筛选要做动画的对象

刷新时调用到 wm services. onTransactionReady 

04-23 18:43:22.529 2073 2100 D jinyanmeiainima: Transition onTransactionReady mToken:Token{a674b32 TransitionRecord{d9c7094 id=6 type=TO_FRONT flags=0}}

04-23 18:43:22.529 2073 2100 D jinyanmeiainima: Transition onTransactionReady info:{t=TO_FRONT f=0x0 ro=Point(0, 0) c=[{WCT{RemoteToken{2a5ead1 Task{c299c5e #39 type=standard A=10245:com..notes}}} m=SHOW f=TRANSLUCENT leash=Surface(name=Task=39)/@0x2c4c3d4 sb=Rect(0, 0 - 1800, 2880) eb=Rect(799, 141 - 1759, 1848) eo=Point(799, 141)}]}
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: Transition onTransactionReady transaction:android.view.SurfaceControl$Transaction@65dbba
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: Transition onTransactionReady mFinishTransaction:android.view.SurfaceControl$Transaction@2d6a6b
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: java.lang.RuntimeException: jinyanmeiainima
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.wm.Transition.onTransactionReady(Transition.java:1215)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.wm.BLASTSyncEngine$SyncGroup.finishNow(BLASTSyncEngine.java:206) //这里会从队列移除
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.wm.BLASTSyncEngine$SyncGroup.onSurfacePlacement(BLASTSyncEngine.java:148)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.wm.BLASTSyncEngine$SyncGroup.-$$Nest$monSurfacePlacement(Unknown Source:0)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.wm.BLASTSyncEngine.onSurfacePlacement(BLASTSyncEngine.java:382)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.wm.RootWindowContainer.performSurfacePlacementNoTrace(RootWindowContainer.java:893)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.wm.RootWindowContainer.performSurfacePlacement(RootWindowContainer.java:836)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacementLoop(WindowSurfacePlacer.java:200)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:149)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.wm.WindowSurfacePlacer.performSurfacePlacement(WindowSurfacePlacer.java:138)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.wm.WindowSurfacePlacer$Traverser.run(WindowSurfacePlacer.java:80)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at android.os.Handler.handleCallback(Handler.java:958)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at android.os.Handler.dispatchMessage(Handler.java:99)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at android.os.Looper.loopOnce(Looper.java:216)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at android.os.Looper.loop(Looper.java:305)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at android.os.HandlerThread.run(HandlerThread.java:67)
04-23 18:43:22.529 2073 2100 D jinyanmeiainima: at com.android.server.ServiceThread.run(ServiceThread.java:46)

BLASTSyncEngine.java
private void finishNow() {
            mActiveSyncs.remove(mSyncId); //从队列移除
 }

Transition.onTransactionReady

mTargets = calculateTargets(mParticipants, mChanges); //计算mTargets

根据target 计算info
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);

Transition.java
public void onTransactionReady(int syncId, SurfaceControl.Transaction transaction) {
             
final ArrayMap<WindowContainer, ChangeInfo> mChanges = new ArrayMap<>(); //这里的mChanges 也是一个map
 
        mTargets = calculateTargets(mParticipants, mChanges);
        final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
        
                mController.getTransitionPlayer().onTransitionReady(
                        mToken, info, transaction, mFinishTransaction);
                if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
                    Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, TRACE_NAME_PLAY_TRANSITION,
             
    }

wm.services.Transition.calculateTargets

两个条件不添加动画:

1:!wc.isAttached()

2:!changeInfo.hasChanged()

将符合条件的changeInfo添加到targets 返回

@VisibleForTesting
@NonNull
static ArrayList<ChangeInfo> calculateTargets(ArraySet<WindowContainer> participants,
        ArrayMap<WindowContainer, ChangeInfo> changes) {
    ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
            "Start calculating TransitionInfo based on participants: %s", participants);
 
    // Add all valid participants to the target container.
    final Targets targets = new Targets();
    for (int i = participants.size() - 1; i >= 0; --i) {
        final WindowContainer<?> wc = participants.valueAt(i);
        if (!wc.isAttached()) {    
            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                    "  Rejecting as detached: %s", wc);
            continue;
        }
        // The level of transition target should be at least window token.
        if (wc.asWindowState() != null) continue;
 
        final ChangeInfo changeInfo = changes.get(wc); // changes即mChanges,由onTransitionReady时添加到changes 的map //这里的changeInfo 与TransitionInfo不是一个
 
        // Reject no-ops
        if (!changeInfo.hasChanged()) {
            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
                    "  Rejecting as no-op: %s", wc);
            continue;
        }
        targets.add(changeInfo);
    }
    ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "  Initial targets: %s",
            targets.mArray);
    // Combine the targets from bottom to top if possible.
    tryPromote(targets, changes);
    // Establish the relationship between the targets and their top changes.
    populateParentChanges(targets, changes);
 
    final ArrayList<ChangeInfo> targetList = targets.getListSortedByZ();
    ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "  Final targets: %s", targetList);
    return targetList;
}

boolean hasChanged() {
         // the task including transient launch must promote to root task
         if ((mFlags & ChangeInfo.FLAG_TRANSIENT_LAUNCH) != 0
                 || (mFlags & ChangeInfo.FLAG_ABOVE_TRANSIENT_LAUNCH) != 0) {
             return true;
         }
         // If it's invisible and hasn't changed visibility, always return false since even if
         // something changed, it wouldn't be a visible change.
         final boolean currVisible = mContainer.isVisibleRequested();
         if (currVisible == mVisible && !mVisible) return false;
         return currVisible != mVisible
                 || mKnownConfigChanges != 0
                 // if mWindowingMode is 0, this container wasn't attached at collect time, so
                 // assume no change in windowing-mode.
                 || (mWindowingMode != 0 && mContainer.getWindowingMode() != mWindowingMode)
                 || !mContainer.getBounds().equals(mAbsoluteBounds)
                 || mRotation != mContainer.getWindowConfiguration().getRotation();
     }

根据上步计算出来的sortedTargets计算要做的动画TransitionInfo

TransitionInfo中有个mChanges

public final class TransitionInfo implements Parcelable {
 
    /**
     * Modes are only a sub-set of all the transit-types since they are per-container
     * @hide
     */
    @IntDef(prefix = { "TRANSIT_" }, value = {
            TRANSIT_NONE,
            TRANSIT_OPEN,
            TRANSIT_CLOSE,
            // Note: to_front/to_back really mean show/hide respectively at the container level.
            TRANSIT_TO_FRONT,
            TRANSIT_TO_BACK,
            TRANSIT_CHANGE
    })
    public @interface TransitionMode {}
    private final ArrayList<Change> mChanges = new ArrayList<>();
public static final class Change implements Parcelable {
    private final WindowContainerToken mContainer;
    private WindowContainerToken mParent;
    private WindowContainerToken mLastParent;
    private final SurfaceControl mLeash;
    private @TransitionMode int mMode = TRANSIT_NONE;
    private @ChangeFlags int mFlags = FLAG_NONE;
    private final Rect mStartAbsBounds = new Rect();
    private final Rect mEndAbsBounds = new Rect();
    private final Point mEndRelOffset = new Point();
    private ActivityManager.RunningTaskInfo mTaskInfo = null;
    private boolean mAllowEnterPip;
    private @Surface.Rotation int mStartRotation = ROTATION_UNDEFINED;
    private @Surface.Rotation int mEndRotation = ROTATION_UNDEFINED;
    /**
     * The end rotation of the top activity after fixed rotation is finished. If the top
     * activity is not in fixed rotation, it will be {@link ROTATION_UNDEFINED}.
     */
    private @Surface.Rotation int mEndFixedRotation = ROTATION_UNDEFINED;
    private int mRotationAnimation = ROTATION_ANIMATION_UNSPECIFIED;
    private @ColorInt int mBackgroundColor;
    private SurfaceControl mSnapshot = null;
    private float mSnapshotLuma;
static TransitionInfo calculateTransitionInfo(@TransitionType int type, int flags,
        ArrayList<ChangeInfo> sortedTargets,
        @Nullable SurfaceControl.Transaction startT) {
    final TransitionInfo out = new TransitionInfo(type, flags);  //根据type 和flags 构建新的TransitionInfo 对象
 
    WindowContainer<?> topApp = null;
    for (int i = 0; i < sortedTargets.size(); i++) {
        final WindowContainer<?> wc = sortedTargets.get(i).mContainer;
        if (!isWallpaper(wc)) {
            topApp = wc;
            break;
        }
    }
    if (topApp == null) {
        out.setRootLeash(new SurfaceControl(), 0, 0);
        return out;
    }
 
    WindowContainer<?> ancestor = findCommonAncestor(sortedTargets, topApp);
 
    // Make leash based on highest (z-order) direct child of ancestor with a participant.
    // TODO(b/261418859): Handle the case when the target contains window containers which
    // belong to a different display. As a workaround we use topApp, from which wallpaper
    // window container is removed, instead of sortedTargets here.
    WindowContainer leashReference = topApp;
    while (leashReference.getParent() != ancestor) {
        leashReference = leashReference.getParent();
    }
    final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName(
            "Transition Root: " + leashReference.getName()).build();
    startT.setLayer(rootLeash, leashReference.getLastLayer());                              // 设置layer
    out.setRootLeash(rootLeash, ancestor.getBounds().left, ancestor.getBounds().top);    //设置leash
 
    // Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order.
    final int count = sortedTargets.size();                      //遍历上一步筛选出来的sortedTargets
    for (int i = 0; i < count; ++i) {
        final ChangeInfo info = sortedTargets.get(i);
        final WindowContainer target = info.mContainer;
        final TransitionInfo.Change change = new TransitionInfo.Change(
                target.mRemoteToken != null ? target.mRemoteToken.toWindowContainerToken()     //根据windwoContainer的成员变量mWindowContainerToken 构建一个change所以每个windwoContainer都有一个change
                        : null, getLeashSurface(target, startT));
        // TODO(shell-transitions): Use leash for non-organized windows.
        if (info.mEndParent != null) {
            change.setParent(info.mEndParent.mRemoteToken.toWindowContainerToken());
        }
        if (info.mStartParent != null && info.mStartParent.mRemoteToken != null
                && target.getParent() != info.mStartParent) {
            change.setLastParent(info.mStartParent.mRemoteToken.toWindowContainerToken());
        }
        change.setMode(info.getTransitMode(target));
        change.setStartAbsBounds(info.mAbsoluteBounds);
        change.setFlags(info.getChangeFlags(target));
 
 
        final Task task = target.asTask();
        final TaskFragment taskFragment = target.asTaskFragment();
        final ActivityRecord activityRecord = target.asActivityRecord();
 
        if (task != null) {
            final ActivityManager.RunningTaskInfo tinfo = new ActivityManager.RunningTaskInfo();
            task.fillTaskInfo(tinfo);
            change.setTaskInfo(tinfo);
            change.setRotationAnimation(getTaskRotationAnimation(task));
            final ActivityRecord topMostActivity = task.getTopMostActivity();
            change.setAllowEnterPip(topMostActivity != null
                    && topMostActivity.checkEnterPictureInPictureAppOpsState());
            final ActivityRecord topRunningActivity = task.topRunningActivity();
            if (topRunningActivity != null && task.mDisplayContent != null
                    // Display won't be rotated for multi window Task, so the fixed rotation
                    // won't be applied. This can happen when the windowing mode is changed
                    // before the previous fixed rotation is applied.
                    && !task.inMultiWindowMode()) {
                // If Activity is in fixed rotation, its will be applied with the next rotation,
                // when the Task is still in the previous rotation.
                final int taskRotation = task.getWindowConfiguration().getDisplayRotation();
                final int activityRotation = topRunningActivity.getWindowConfiguration()
                        .getDisplayRotation();
                if (taskRotation != activityRotation) {
                    change.setEndFixedRotation(activityRotation);
                }
            }
        } else if ((info.mFlags & ChangeInfo.FLAG_SEAMLESS_ROTATION) != 0) {
            change.setRotationAnimation(ROTATION_ANIMATION_SEAMLESS);
        }
 
        final WindowContainer<?> parent = target.getParent();
        final Rect bounds = target.getBounds();
        final Rect parentBounds = parent.getBounds();
        change.setEndRelOffset(bounds.left - parentBounds.left,
                bounds.top - parentBounds.top);
        int endRotation = target.getWindowConfiguration().getRotation();
        if (activityRecord != null) {
            // TODO(b/227427984): Shell needs to aware letterbox.
            // Always use parent bounds of activity because letterbox area (e.g. fixed aspect
            // ratio or size compat mode) should be included in the animation.
            change.setEndAbsBounds(parentBounds);
            if (activityRecord.getRelativeDisplayRotation() != 0
                    && !activityRecord.mTransitionController.useShellTransitionsRotation()) {
                // Use parent rotation because shell doesn't know the surface is rotated.
                endRotation = parent.getWindowConfiguration().getRotation();
            }
        } else {
            change.setEndAbsBounds(bounds);
        }
 
        if (activityRecord != null || (taskFragment != null && taskFragment.isEmbedded())) {
            final int backgroundColor;
            final TaskFragment organizedTf = activityRecord != null
                    ? activityRecord.getOrganizedTaskFragment()
                    : taskFragment.getOrganizedTaskFragment();
            if (organizedTf != null && organizedTf.getAnimationParams()
                    .getAnimationBackgroundColor() != DEFAULT_ANIMATION_BACKGROUND_COLOR) {
                // This window is embedded and has an animation background color set on the
                // TaskFragment. Pass this color with this window, so the handler can use it as
                // the animation background color if needed,
                backgroundColor = organizedTf.getAnimationParams()
                        .getAnimationBackgroundColor();
            } else {
                // Set background color to Task theme color for activity and embedded
                // TaskFragment in case we want to show background during the animation.
                final Task parentTask = activityRecord != null
                        ? activityRecord.getTask()
                        : taskFragment.getTask();
                backgroundColor = parentTask.getTaskDescription().getBackgroundColor();
            }
            // Set to opaque for animation background to prevent it from exposing the blank
            // background or content below.
            change.setBackgroundColor(ColorUtils.setAlphaComponent(backgroundColor, 255));
        }
 
        change.setRotation(info.mRotation, endRotation);
        if (info.mSnapshot != null) {
            change.setSnapshot(info.mSnapshot, info.mSnapshotLuma);
        }
 
        out.addChange(change);  //最后把widowContainer的change放入mChanger中out(TransitionInfo)中
    }
 
    TransitionInfo.AnimationOptions animOptions = null;
    if (topApp.asActivityRecord() != null) {
        final ActivityRecord topActivity = topApp.asActivityRecord();
        animOptions = addCustomActivityTransition(topActivity, true/* open */, null);
        animOptions = addCustomActivityTransition(topActivity, false/* open */, animOptions);
    }
    final WindowManager.LayoutParams animLp =
            getLayoutParamsForAnimationsStyle(type, sortedTargets);
    if (animLp != null && animLp.type != TYPE_APPLICATION_STARTING
            && animLp.windowAnimations != 0) {
        // Don't send animation options if no windowAnimations have been set or if the we are
        // running an app starting animation, in which case we don't want the app to be able to
        // change its animation directly.
        if (animOptions != null) {
            animOptions.addOptionsFromLayoutParameters(animLp);
        } else {
            animOptions = TransitionInfo.AnimationOptions
                    .makeAnimOptionsFromLayoutParameters(animLp);
        }
    }
    if (animOptions != null) {
        out.setAnimationOptions(animOptions);
    }

    return out;
}

2.2 开始动画

到shell.onTransitionReady

@BinderThread
private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
    @Override
    public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
            SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
            throws RemoteException {
        mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
                iBinder, transitionInfo, t, finishT));
    }
 
    @Override
    public void requestStartTransition(IBinder iBinder,
            TransitionRequestInfo request) throws RemoteException {
        mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request));
    }
}
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
            @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
                transitionToken, info);
        final int activeIdx = findByToken(mPendingTransitions, transitionToken);
        if (activeIdx < 0) {
            throw new IllegalStateException("Got transitionReady for non-pending transition "
                    + transitionToken + ". expecting one of "
                    + Arrays.toString(mPendingTransitions.stream().map(
                            activeTransition -> activeTransition.mToken).toArray()));
        }
        if (activeIdx > 0) {
            Log.e(TAG, "Transition became ready out-of-order " + transitionToken + ". Expected"
                    + " order: " + Arrays.toString(mPendingTransitions.stream().map(
                            activeTransition -> activeTransition.mToken).toArray()));
        }
        // Move from pending to ready
        final ActiveTransition active = mPendingTransitions.remove(activeIdx);//从 mPendingTransitions 移除
        mReadyTransitions.add(active); //添加至 mReadyTransitions
        active.mInfo = info;
        active.mStartT = t;
        active.mFinishT = finishT;
 
        for (int i = 0; i < mObservers.size(); ++i) {
            mObservers.get(i).onTransitionReady(transitionToken, info, t, finishT);
        }
 
        if (info.getType() == TRANSIT_SLEEP) {
            if (activeIdx > 0) {
                if (!info.getRootLeash().isValid()) {
                    // Shell has some debug settings which makes calling binders with invalid
                    // surfaces crash, so replace it with a "real" one.
                    info.setRootLeash(new SurfaceControl.Builder().setName("Invalid")
                            .setContainerLayer().build(), 0, 0);
                }
                // Sleep starts a process of forcing all prior transitions to finish immediately
                finishForSleep(null /* forceFinish */);
                return;
            }
        }
 
        // Allow to notify keyguard un-occluding state to KeyguardService, which can happen while
        // screen-off, so there might no visibility change involved.
        if (!info.getRootLeash().isValid() && info.getType() != TRANSIT_KEYGUARD_UNOCCLUDE) {
            // Invalid root-leash implies that the transition is empty/no-op, so just do
            // housekeeping and return.
            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s",
                    transitionToken, info);
            onAbort(active);
            return;
        }
 
        final int changeSize = info.getChanges().size(); //找到带有change的info
        boolean taskChange = false;
        boolean transferStartingWindow = false;
        boolean allOccluded = changeSize > 0;
        for (int i = changeSize - 1; i >= 0; --i) {
            final TransitionInfo.Change change = info.getChanges().get(i);
            taskChange |= change.getTaskInfo() != null;
            transferStartingWindow |= change.hasFlags(FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT);
            if (!change.hasFlags(FLAG_IS_OCCLUDED)) {
                allOccluded = false;
            }
        }
        // There does not need animation when:
        // A. Transfer starting window. Apply transfer starting window directly if there is no other
        // task change. Since this is an activity->activity situation, we can detect it by selecting
        // transitions with only 2 changes where neither are tasks and one is a starting-window
        // recipient.
        if (!taskChange && transferStartingWindow && changeSize == 2
                // B. It's visibility change if the TRANSIT_TO_BACK/TO_FRONT happened when all
                // changes are underneath another change.
                || ((info.getType() == TRANSIT_TO_BACK || info.getType() == TRANSIT_TO_FRONT)
                && allOccluded)) {
            // Treat this as an abort since we are bypassing any merge logic and effectively
            // finishing immediately.
            onAbort(active);
            return;
        }
 
        setupStartState(active.mInfo, active.mStartT, active.mFinishT);    //开始置状态
 
        if (mReadyTransitions.size() > 1) {
            // There are already transitions waiting in the queue, so just return.
            return;
        }
        processReadyQueue();  //开始动画
    }
04-23 18:44:02.339 2107 2246 D jinyanmeiainima: BLASTSyncEngine finishNow s.mSyncId:5
04-23 18:44:02.339 2107 2246 D jinyanmeiainima: BLASTSyncEngine finishNow mActiveSyncs++++:1
04-23 18:44:02.339 2107 2246 D jinyanmeiainima: BLASTSyncEngine finishNow s.mSyncId:5
04-23 18:44:02.339 2107 2246 D jinyanmeiainima: BLASTSyncEngine finishNow mActiveSyncs++++:0
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: startAnimation TRANSIT_CHANGE:
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: java.lang.RuntimeException: jinyanmeiainima
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at com.android.wm.shell.transition.Transitions.dispatchTransition(Transitions.java:708)
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at com.android.wm.shell.transition.Transitions.playTransition(Transitions.java:693)
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at com.android.wm.shell.transition.Transitions.processReadyQueue(Transitions.java:627)
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at com.android.wm.shell.transition.Transitions.onTransitionReady(Transitions.java:600)
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl.lambda$onTransitionReady$0(Transitions.java:1122)
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl.$r8$lambda$qsRfWn1ItrZqnFeABBdxU50jPc4(Unknown Source:0)
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at com.android.wm.shell.transition.Transitions$TransitionPlayerImpl$$ExternalSyntheticLambda0.run(Unknown Source:10)
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at android.os.Handler.handleCallback(Handler.java:958)
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at android.os.Handler.dispatchMessage(Handler.java:99)
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at android.os.Looper.loopOnce(Looper.java:216)
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at android.os.Looper.loop(Looper.java:305)
04-23 18:44:02.347 3002 3197 D jinyanmeiainima: at android.os.HandlerThread.run(HandlerThread.java:67)

然后各个继承于

implements Transitions.TransitionHandler 的对象都会调用到startAnimation

走各个业务动画逻辑

  • 16
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我会为您解答关于html中transition属性和CSS3中Transition动画属性的用法。 在HTML中,transition属性表示元素在一定时间内从一种样式转换为另一种样式的过程。transition属性需要指定以下几个参数: - transition-property:指定过渡的CSS属性,可以使用all来表示所有属性都过渡。 - transition-duration:指定过渡的时间,以秒或毫秒为单位。 - transition-timing-function:指定过渡的时间函数,可以使用一些预定义的函数,如linear、ease、ease-in、ease-out等。 - transition-delay:指定过渡的延迟时间,以秒或毫秒为单位。 举个例子,如果您想让一个元素在鼠标悬停时变成红色,可以这样写: ``` div { background-color: blue; transition-property: background-color; transition-duration: 1s; transition-timing-function: ease-in-out; } div:hover { background-color: red; } ``` 这样,在鼠标悬停时,该元素的背景色会从蓝色平滑地过渡到红色。 在CSS3中,Transition动画属性是与transition属性相关的一组属性,包括以下几个参数: - transition-property:同HTML中的transition-property参数。 - transition-duration:同HTML中的transition-duration参数。 - transition-timing-function:同HTML中的transition-timing-function参数。 - transition-delay:同HTML中的transition-delay参数。 - transition: 简写形式,可以一次性指定以上所有参数。例如:transition: background-color 1s ease-in-out 0s。 此外,CSS3中还新增了一些过渡效果,包括旋转、缩放、平移等。举个例子,如果您想让一个元素在鼠标悬停时旋转180度并缩小一半,可以这样写: ``` div { width: 100px; height: 100px; background-color: blue; transition: transform 1s ease-in-out; } div:hover { transform: rotate(180deg) scale(0.5); } ``` 这样,在鼠标悬停时,该元素会以一个180度的旋转和50%的缩放平滑地过渡到新的状态。 希望这些内容能够帮到您,如果您还有其他问题,请随时提出。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值