StartingWindow要想进入自定义动画,需要在 com/android/server/wm/ActivityRecord.java
void removeStartingWindow() {
boolean prevEligibleForLetterboxEducation = isEligibleForLetterboxEducation();
if (transferSplashScreenIfNeeded()) {
// 如果需要传递splashScreen到应用端,就返回了
return;
}
removeStartingWindowAnimation(true /* prepareAnimation */);
final Task task = getTask();
if (prevEligibleForLetterboxEducation != isEligibleForLetterboxEducation()
&& task != null) {
// Trigger TaskInfoChanged to update the letterbox education.
task.dispatchTaskInfoChangedIfNeeded(true /* force */);
}
}
需要transferSplashScreenIfNeeded返回true,不然就走下面的老流程了。
com/android/server/wm/ActivityRecord.java
private boolean transferSplashScreenIfNeeded() {
if (finishing || !mHandleExitSplashScreen || mStartingSurface == null
|| mStartingWindow == null
|| mTransferringSplashScreenState == TRANSFER_SPLASH_SCREEN_FINISH
// skip copy splash screen to client if it was resized
|| (mStartingData != null && mStartingData.mResizedFromTransfer)) {
return false;
}
if (isTransferringSplashScreen()) {
return true;
}
// Only do transfer after transaction has done when starting window exist.
if (mStartingData != null && mStartingData.mWaitForSyncTransactionCommit) {
mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_COPY_TO_CLIENT;
return true;
}
requestCopySplashScreen();
return isTransferringSplashScreen();
}
我们activity刚打开finishing不可能为true,mStartingSurface和mStartingWindow是startingwindow创建的时候赋值的不为空,mTransferringSplashScreenState一直没有操作过所以不可能为TRANSFER_SPLASH_SCREEN_FINISH,mResizedFromTransfer是app resize相关的,也不是true。所以关键就是mHandleExitSplashScreen
发现只有一个地方赋值的。
调用关系:
ActivityRecord#setCustomizeSplashScreenExitAnimation
ActivityRecord#activityResumedLocked
ActivityClientController#activityResumed
ActivityClient#activityResumed
ResumeActivityItem#postExecute
android/app/servertransaction/ResumeActivityItem.java
public void postExecute(@NonNull ClientTransactionHandler client,
@NonNull PendingTransactionActions pendingActions) {
// TODO(lifecycler): Use interface callback instead of actual implementation.
ActivityClient.getInstance().activityResumed(getActivityToken(),
client.isHandleSplashScreenExit(getActivityToken()));
}
就是对应client.isHandleSplashScreenExit(getActivityToken())的值,继续跟进
android/app/ActivityThread.java
public boolean isHandleSplashScreenExit(@NonNull IBinder token) {
synchronized (this) {
return mSplashScreenGlobal != null && mSplashScreenGlobal.containsExitListener(token);
}
}
android/window/SplashScreen.java
public boolean containsExitListener(IBinder token) {
synchronized (mGlobalLock) {
final SplashScreenImpl impl = findImpl(token);
return impl != null && impl.mExitAnimationListener != null;
}
}
impl.mExitAnimationListener的赋值 android/window/SplashScreen.java#SplashScreenImpl
public void setOnExitAnimationListener(
@NonNull SplashScreen.OnExitAnimationListener listener) {
if (mActivityToken == null) {
// This is not an activity.
return;
}
synchronized (mGlobal.mGlobalLock) {
if (listener != null) {
// 这边赋值的
mExitAnimationListener = listener;
mGlobal.addImpl(this);
}
}
}
这个setOnExitAnimationListener就是app侧设置的,参考官方文档 developer.android.com/develop/ui/…
getSplashScreen().setOnExitAnimationListener(splashScreenView -> {
final ObjectAnimator slideUp = ObjectAnimator.ofFloat(
splashScreenView,
View.TRANSLATION_Y,
0f,
-splashScreenView.getHeight()
);
slideUp.setInterpolator(new AnticipateInterpolator());
slideUp.setDuration(200L);
slideUp.start();
});
这边就清楚了,app侧调用getSplashScreen().setOnExitAnimationListener设置回调,会把systemServer侧的ActivityRecord的mHandleExitSplashScreen设置为true,这样transferSplashScreenIfNeeded就可以进行下去执行requestCopySplashScreen,后续详细流程可以看juejin.cn/post/750891…