目前Google已经释放了Android Q
平台的SDK, 有OEM手机厂商在新机器中已经使用了Android Q的系统, 一些App应用也已经开始适配了Android Q系统.本文档主要说明在Android Studio工程中,如果target SDK适配的是Android Q系统时, Support library配置需要注意的一个潜在问题.
理论上support library的版本号可以低于target SDK的版本号, 比如target SDK是28(Android P系统), 可以配置support library为27.0.1,但是最好还是根据Google官方文档配置指定版本的support library.当target SDK升级到29(Android Q系统)并且support library是27.0.1时, Android Framework
将会存在一个潜在的问题, 有可能会导致App产生bug. 这个Android底层的潜在问题是这样的:
Support library27.0.1中的如下函数(FragmentManager.java
):
/**
* Returns an existing AnimationListener on an Animation or {@code null} if none exists.
*/
private static AnimationListener getAnimationListener(Animation animation) {
AnimationListener originalListener = null;
try {
if (sAnimationListenerField == null) {
sAnimationListenerField = Animation.class.getDeclaredField("mListener");
sAnimationListenerField.setAccessible(true);
}
originalListener = (AnimationListener) sAnimationListenerField.get(animation);
} catch (NoSuchFieldException e) {
Log.e(TAG, "No field with the name mListener is found in Animation class", e);
} catch (IllegalAccessException e) {
Log.e(TAG, "Cannot access Animation's mListener field", e);
}
return originalListener;
}
运行在Android Q手机上时, 由于类Animation
中不再存在mListener
成员变量, 因此反射调用结果为null. 该函数getAnimationListener
被如下函数使用(FragmentManager.java
):
/**
* Animates the removal of a fragment with the given animator or animation. After animating,
* the fragment's view will be removed from the hierarchy.
*
* @param fragment The fragment to animate out
* @param anim The animator or animation to run on the fragment's view
* @param newState The final state after animating.
*/
private void animateRemoveFragment(@NonNull final Fragment fragment,
@NonNull AnimationOrAnimator anim, final int newState) {
final View viewToAnimate = fragment.mView;
final ViewGroup container = fragment.mContainer;
container.startViewTransition(viewToAnimate);
fragment.setStateAfterAnimating(newState);
if (anim.animation != null) {
Animation animation = anim.animation;
fragment.setAnimatingAway(fragment.mView);
AnimationListener listener = getAnimationListener(animation);
animation.setAnimationListener(new AnimationListenerWrapper(listener) {
@Override
public void onAnimationEnd(Animation animation) {
super.onAnimationEnd(animation);
// onAnimationEnd() comes during draw(), so there can still be some
// draw events happening after this call. We don't want to detach
// the view until after the onAnimationEnd()
container.post(new Runnable() {
@Override
public void run() {
container.endViewTransition(viewToAnimate);
if (fragment.getAnimatingAway() != null) {
fragment.setAnimatingAway(null);
moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0,
false);
}
}
});
}
});
setHWLayerAnimListenerIfAlpha(viewToAnimate, anim);
fragment.mView.startAnimation(animation);
} else {
Animator animator = anim.animator;
fragment.setAnimator(anim.animator);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator anim) {
container.endViewTransition(viewToAnimate);
// If an animator ends immediately, we can just pretend there is no animation.
// When that happens the the fragment's view won't have been removed yet.
Animator animator = fragment.getAnimator();
fragment.setAnimator(null);
if (animator != null && container.indexOfChild(viewToAnimate) < 0) {
moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0, false);
}
}
});
animator.setTarget(fragment.mView);
setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
animator.start();
}
}
类AnimationListenerWrapper
通过AnimationListener
对象(函数getAnimationListener返回的)进行对象构造, 但是当传入的这个参数为null时,AnimationListenerWrapper对象将不会调用其onAnimationEnd
方法, 而该方法中会对Fragment
的状态进行更新.Android Framework
的这个情况将会导致App中使用Fragment时, 有可能存在功能异常(尽管App中的代码逻辑没有任何bug).
结论:
当Android工程的target SDK升级到Android Q系统时, 需要将support library配置为28.0.0. 或者使用Androidx
包. Androidx是support library的超集, support library28.0.0是support library的最后一个版本, 后续将使用Androidx实现相关功能.