如果类实现了更新的接口,Android推荐的安全支持更新api的方法有错误。 为什么?

为了支持不同的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; } 

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

这非常有效。

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

所以我的问题是,为什么会发生这种情况? 我的印象是,使用这种技术,在第一次直接引用之前,不会加载较新的类。 但是,如果你添加一个接口,它似乎尝试加载它甚至不调用detector = new FroyoDetector(context); 线。

以下是一些重现问题的代码:

这是在针对sdk 16的应用中,最小值为8.在2.3设备上运行此function会重现此问题。

这是三个类:

 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; 

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

是的,我可以重现这个问题。 但是,有点令人惊讶的是,正如你所注意到的那样,事实上它没有崩溃意味着这更像是一个案例,因为Dalvik在LogCat中可能比应该对应用程序造成伤害的任何事情都要过于繁琐。

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

 @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的一个大问题。