接上一篇文章 Target SDK 升级到 29 AnimatorSet 动画不执行了 问题咱们修好了,可是有个奇怪的点不知道咱们有没有注意到。
上一篇文章咱们知道了 AnimatorSet
没有执行的原因是因为 sDurationScale
反射失利导致的。可是文章最后有一个很奇怪的点,为什么 Application
中设置 sDurationScale
不生效?? Activity
中设置才有作用???不知道咱们有没有关注这个点?
上篇文章遗留问题
- 为什么
Application
中设置sDurationScale
无效? - 为什么
Activity
中设置才有用? - 体系
sDurationScale
又是何时设置这个值的呢?省电形式切换对这个值有些什么影响呢? - 还有没有其他的坑点?
带着这些问题咱们看下为什么,找寻下其根本原因
分析进程
咱们看下 setDurationScale
调用方位
从 Android
源码搜索网站上咱们能够看到 WindowManagerGlobal
和 WindowManagerService.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
我把代码都贴了下,总结下详细有以下几个地方
-
WindowManagerGlobal
类调用getWindowManagerService()
获取WindowManagerService
对象时 -
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
强制打开了动画这个时分是正常的。可是如果用户设置省电形式或者进入了低电量的状态,体系就会回调 WindowManagerService
的 onLowPowerModeChanged
导致 sDurationScale
重新被设置,这个时分用户没有关闭咱们的 App
而是边充电边玩,就还是会出现 sDurationScale == 0F
导致动画不执行。
上述这种状况就是一个坑点了!!! 那么怎样去解决这个问题,这个仁者见仁智者见智了,目前个人想到的方案有以下几种
-
onLowPowerModeChanged
测验监听这个,在这个中重新设置 -
start()
时分每次都去设置 - 和产品友好沟通一波,究竟这个玩意儿是
google
参加灰名单的玩意儿。保不齐xxxx,这种东西咱不懂咱也不好说。
剩余 windowAddedLocked
是时分的影响,这边咱们自己看下吧,不细说了
OK, 整篇文章到此结束。我要上分去了。。。。说的不对的自己谈论去找大佬去吧。