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