Чтобы поддерживать разные уровни 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 (в примере Case, 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;
Он не сработает, и он будет продолжать работать правильно.