关于Android Q平台的support库的配置

目前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实现相关功能.

你可能感兴趣的:(Android)