为了支持不同的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),它可以使用Froyo类来访问较新版本的API。对于较旧的设备,他们收到的类仅使用较旧的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;

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

最佳答案

是的,我可以重现该问题。但是,正如您所指出的那样,Kinda令人惊讶的事实是它不会崩溃,这意味着Dalvik在LogCat中可能比其他对应用程序有害的东西显得过于健谈。

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

@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而言不是一个大问题。

关于java - 如果该类实现了较新的接口(interface),则Android建议的安全支持较新api的方法将出错。为什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/13103902/

10-10 04:46