接上一篇文章 Target SDK 升级到 29 AnimatorSet 动画不执行了 问题咱们修好了,可是有个奇怪的点不知道咱们有没有注意到。

上一篇文章咱们知道了 AnimatorSet 没有执行的原因是因为 sDurationScale 反射失利导致的。可是文章最后有一个很奇怪的点,为什么 Application 中设置 sDurationScale 不生效?? Activity 中设置才有作用???不知道咱们有没有关注这个点?

上篇文章遗留问题

  1. 为什么 Application 中设置 sDurationScale 无效?
  2. 为什么 Activity 中设置才有用?
  3. 体系 sDurationScale 又是何时设置这个值的呢?省电形式切换对这个值有些什么影响呢?
  4. 还有没有其他的坑点?

带着这些问题咱们看下为什么,找寻下其根本原因

分析进程

咱们看下 setDurationScale 调用方位

Target SDK 升级到 29 AnimatorSet 动画不执行了(后续)

Android 源码搜索网站上咱们能够看到 WindowManagerGlobalWindowManagerService.class 有几处调用方位

WindowManagerGlobal.class

public final class WindowManagerGlobal {
    @UnsupportedAppUsage
    public static void initialize() {
        getWindowManagerService();
    }
    @UnsupportedAppUsage
    public static IWindowManager getWindowManagerService() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowManagerService == null) {
                sWindowManagerService = IWindowManager.Stub.asInterface(
                        ServiceManager.getService("window"));
                try {
                    if (sWindowManagerService != null) {
                        ValueAnimator.setDurationScale(
                                sWindowManagerService.getCurrentAnimatorScale());
                        sUseBLASTAdapter = sWindowManagerService.useBLAST();
                    }
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowManagerService;
        }
    }
    @UnsupportedAppUsage
    public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
                    // Emulate the legacy behavior.  The global instance of InputMethodManager
                    // was instantiated here.
                    // TODO(b/116157766): Remove this hack after cleaning up @UnsupportedAppUsage
                    InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            });
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }
}

WindowManagerService.class

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs,
                DisplayManagerService.WindowManagerFuncs, DisplayManager.DisplayListener {
   private WindowManagerService(Context context, PowerManagerService pm,
            DisplayManagerService displayManager, InputManagerService inputManager,
            Handler uiHandler,
            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {
        // 省掉部分代码
        if (mPowerManagerInternal != null) {
            mPowerManagerInternal.registerLowPowerModeObserver(
                    new PowerManagerInternal.LowPowerModeListener() {
                @Override
                public int getServiceType() {
                    return ServiceType.ANIMATION;
                }
                @Override
                public void onLowPowerModeChanged(PowerSaveState result) {
                    synchronized (mGlobalLock) {
                        final boolean enabled = result.batterySaverEnabled;
                        if (mAnimationsDisabled != enabled && !mAllowAnimationsInLowPowerMode) {
                            mAnimationsDisabled = enabled;
                            dispatchNewAnimatorScaleLocked(null);
                        }
                    }
                }
            });
            mAnimationsDisabled = mPowerManagerInternal
                    .getLowPowerState(ServiceType.ANIMATION).batterySaverEnabled;
        }
        // 省掉部分代码
        setAnimatorDurationScale(getAnimatorDurationScaleSetting());
        // 省掉部分代码
    }
    void windowAddedLocked() {
        // 省掉部分代码
        if (mSurfaceSession == null) {
            // 省掉部分代码
            if (mLastReportedAnimatorScale != mService.getCurrentAnimatorScale()) {
                mService.dispatchNewAnimatorScaleLocked(this);
            }
        }
        mNumWindow++;
    }
    void dispatchNewAnimatorScaleLocked(Session session) {
        mH.obtainMessage(H.NEW_ANIMATOR_SCALE, session).sendToTarget();
    }
    @Override
    public void handleMessage(Message msg) {
        if (DEBUG_WINDOW_TRACE) {
            Slog.v(TAG_WM, "handleMessage: entry what=" + msg.what);
        }
        case NEW_ANIMATOR_SCALE: {
            float scale = getCurrentAnimatorScale();
            ValueAnimator.setDurationScale(scale);
            Session session = (Session)msg.obj;
            if (session != null) {
                try {
                	session.mCallback.onAnimatorScaleChanged(scale);
                } catch (RemoteException e) {
                }
            } else {
                ArrayList<IWindowSessionCallback> callbacks
                        = new ArrayList<IWindowSessionCallback>();
                synchronized (mGlobalLock) {
                    for (int i=0; i<mSessions.size(); i++) {
                        callbacks.add(mSessions.valueAt(i).mCallback);
                    }
                }
                for (int i=0; i<callbacks.size(); i++) {
                    try {
                        callbacks.get(i).onAnimatorScaleChanged(scale);
                    } catch (RemoteException e) {
                    }
                }
            }
            break;
       }
   }
}

这边能能影响到 sDurationScale 我把代码都贴了下,总结下详细有以下几个地方

  1. WindowManagerGlobal 类调用 getWindowManagerService() 获取 WindowManagerService 对象时
  2. openSession 时设置的回调会影响到
    • windowAddedLocked 因为篇幅有限有兴趣的能够去看下这个 Android 源码 图形体系之 WindowState attach
    • onLowPowerModeChanged 这个就很显着了低电量的时分会回调

这边咱们稍微看下 WindowManagerGlobal 能够看到初始化方位在 handleLaunchActivity

public final class ActivityThread extends ClientTransactionHandler
        implements ActivityThreadInternal {
    @Override
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        // 省掉部分代码
        WindowManagerGlobal.initialize();
		// 省掉部分代码
        return a;
    }
}

看到这儿根底好的应该就能理解为什么 Application 中设置 sDurationScale 无效,而 Activity 设置 sDurationScale 有用了。不太理解的能够去搜索 handleLaunchActivity 调用机遇,因为篇幅有限不做过多介绍。这边对启动流程不熟悉的话推荐看下这篇文章 # Android Application创立到Activity启动(launcher启动和startActivity启动)

这边就解说了完了前面两个疑问的,剩余就看看有没有其他的坑点。虽然咱们在 Activity 中设置 sDurationScale 修复了动画不播放的问题,可是不知道有没有人记住 openSession 设置的回调。

假定当时有这么个场景:用户开发者形式中设置了关闭动画,而咱们在 Activity 强制打开了动画这个时分是正常的。可是如果用户设置省电形式或者进入了低电量的状态,体系就会回调 WindowManagerServiceonLowPowerModeChanged 导致 sDurationScale 重新被设置,这个时分用户没有关闭咱们的 App 而是边充电边玩,就还是会出现 sDurationScale == 0F 导致动画不执行。

上述这种状况就是一个坑点了!!! 那么怎样去解决这个问题,这个仁者见仁智者见智了,目前个人想到的方案有以下几种

  1. onLowPowerModeChanged 测验监听这个,在这个中重新设置
  2. start() 时分每次都去设置
  3. 产品友好沟通一波,究竟这个玩意儿是 google 参加灰名单的玩意儿。保不齐xxxx,这种东西咱不懂咱也不好说。

剩余 windowAddedLocked 是时分的影响,这边咱们自己看下吧,不细说了

OK, 整篇文章到此结束。我要上分去了。。。。说的不对的自己谈论去找大佬去吧。