3

为了支持不同的 Api 级别,我使用了这里描述的技术:http://android-developers.blogspot.com/2010/07/how-to-have-your-cupcake-and-eat-it-too。 html

这是文章中的示例:

public static VersionedGestureDetector newInstance(Context context,
        OnGestureListener listener) {
    final int sdkVersion = Integer.parseInt(Build.VERSION.SDK);
    VersionedGestureDetector detector = null;
    if (sdkVersion < Build.VERSION_CODES.ECLAIR) {
        detector = new CupcakeDetector();
    } else if (sdkVersion < Build.VERSION_CODES.FROYO) {
        detector = new EclairDetector();
    } else {
        detector = new FroyoDetector(context);
    }

    detector.mListener = listener;

    return detector;
}

这种方法“利用了 ClassLoader 的惰性”。对于具有较新 API 级别的设备(在示例中为 Froyo),它可以使用访问较新版本 API 的 Froyo 类。对于较旧的设备,它们会收到一个仅使用较旧 API 的类。

这完美地工作。

但是,如果您让 FroyoDetector 实现一个仅存在于较新的 api 级别中的接口,则当调用 newInstance() 时,即使在它运行该方法中的任何代码之前,它也会尝试加载 FroyoDetector 实现并放置的接口类日志中显示无法加载 FroyoDetector 类的错误。

所以我的问题是,为什么会发生这种情况?我的印象是,使用这种技术,直到第一次直接引用新类才会加载。detector = new FroyoDetector(context);但是,如果您向其添加接口,即使不调用该行,它似乎也会尝试加载它。

这是一些重现该问题的代码:

这是一个针对 sdk 16 的应用程序,最小为 8。在 2.3 设备上运行它会重现该问题。

以下是三个类:

public class VersionedLoader {

    public static VersionedLoader newInstance() {
        if (Build.VERSION.SDK_INT < 12) {
            return new OldVersionLoader();
        } else {
            return new NewVersionLoader();
        }
    }

}

-

public class OldVersionLoader extends VersionedLoader {

}

-

@TargetApi(11)
public class NewVersionLoader extends VersionedLoader implements AnimatorListener {

    @Override
    public void onAnimationStart(Animator animation) {}

    @Override
    public void onAnimationEnd(Animator animation) {}

    @Override
    public void onAnimationCancel(Animator animation) {}

    @Override
    public void onAnimationRepeat(Animator animation) {}

}

AnimatorListener 仅从 3.1 开始可用。

现在如果你运行:Object obj = VersionedLoader.newInstance();

此错误将出现在日志中:

10-27 13:51:14.437: I/dalvikvm(7673): Failed resolving Lyour/package/name/NewVersionLoader; interface 7 'Landroid/animation/Animator$AnimatorListener;'
10-27 13:51:14.437: W/dalvikvm(7673): Link of class 'Lyour/package/name/NewVersionLoader;' failed
10-27 13:51:14.445: E/dalvikvm(7673): Could not find class 'your.package.name.NewVersionLoader', referenced from method your.package.name.VersionedLoader.newInstance
10-27 13:51:14.445: W/dalvikvm(7673): VFY: unable to resolve new-instance 1327 (Lyour/package/name/NewVersionLoader;) in Lyour/package/name/VersionedLoader;
10-27 13:51:14.445: D/dalvikvm(7673): VFY: replacing opcode 0x22 at 0x000c
10-27 13:51:14.445: D/dalvikvm(7673): VFY: dead code 0x000e-0011 in Lyour/package/name/VersionedLoader;.newInstance ()Lyour/package/name/VersionedLoader;

它不会崩溃,它实际上会继续正常工作。

4

1 回答 1

4

是的,我可以重现该问题。有点令人惊讶,不过,正如您所指出的,它没有崩溃的事实意味着这更像是 Dalvik 在 LogCat 中可能有点过于健谈,而不是任何应该对应用程序造成伤害的事情。

一种解决方法是将接口移动到内部类。在您的示例中,内部类不是NewVersionLoader实现,而是实现:AnimatorListenerNewVersionLoaderAnimationListener

@TargetApi(11)
public class NewVersionLoader extends VersionedLoader {
    private class Foo implements AnimatorListener {
        @Override
        public void onAnimationStart(Animator animation) {}

        @Override
        public void onAnimationEnd(Animator animation) {}

        @Override
        public void onAnimationCancel(Animator animation) {}

        @Override
        public void onAnimationRepeat(Animator animation) {}

    }
}

诚然,这可能并不理想,具体取决于您对VersionedLoader. 但是,由于VersionedLoader它本身没有实现,AnimationListener用户不会VersionedLoader调用AnimationListener方法,所以你的逻辑在内部类而不是实际类上的事实不应该是一个大问题 AFAIK。

于 2012-10-28T18:42:42.473 回答