http://androidappdocs.appspot.com/resources/articles/backward-compatibility.html
前面一段话的意思是:现在越来越多的android设备了,在这些设备中有很多android版本,有些运行最新的,有些运行着很旧的版本,作为一个开发者,你需要考虑你的程序向后兼容。
设置最低SDK版本
如果你需要录制视频的API(API Level 3),那么需要在程序的manifest文件中添加<android:minSdkVersion>标签来确保你的程序不被安装在更旧的android设备上,例如,如果你的程序基于API Level 3,那么你可以指定最低SDK版本为3L:
<manifest> ... <uses-sdk android:minSdkVersion="3" /> ... </manifest>
但是如果你想要添加一个有用但是不重要的功能,例如即使是在有物理键盘的情况下弹出一个虚拟键盘,你可以无误的在旧设备上使用新功能
使用反射机制
假如你要使用一个简单的新调用,像android.os.Debug.dumpHprofData(String filename),Debug 类在android1.0上就已经存在了,但是这个方法在Android 1.5上才开放,如果直接调用它,程序会在android 1.1或者更早的设备上运行失败。
最简单的方法是通过反射机制来调用它,这需要做一次查看和缓存Method对象的结果,使用method的原因是要用到Method.invoke这个方法和它的结果,参考下面代码
public class Reflect { private static Method mDebug_dumpHprofData; static { initCompatibility(); }; private static void initCompatibility() { try { mDebug_dumpHprofData = Debug.class.getMethod( "dumpHprofData", new Class[] { String.class } ); /* success, this is a newer device */ } catch (NoSuchMethodException nsme) { /* failure, must be older device */ } } private static void dumpHprofData(String fileName) throws IOException { try { mDebug_dumpHprofData.invoke(null, fileName); } catch (InvocationTargetException ite) { /* unpack original exception when possible */ Throwable cause = ite.getCause(); if (cause instanceof IOException) { throw (IOException) cause; } else if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } else if (cause instanceof Error) { throw (Error) cause; } else { /* unexpected checked exception; wrap and re-throw */ throw new RuntimeException(ite); } } catch (IllegalAccessException ie) { System.err.println("unexpected " + ie); } } public void fiddle() { if (mDebug_dumpHprofData != null) { /* feature is supported */ try { dumpHprofData("/sdcard/dump.hprof"); } catch (IOException ie) { System.err.println("dump failed!"); } } else { /* feature not supported, do something else */ System.out.println("dump not supported"); } } }
使用静态初始化程序调用initCompatibility来查看方法,如果成功,使用一个和原来方法一模一样的私有方法来完成调用,模仿原来的方法返回值(如果有)或者抛出异常,示例中的fiddle方法显示了,应用程序如何选择调用新的api或者使用新的方法做一些不同的操作
每增加一个你想要调用的方法,你需要添加一个私有的Method字段,字段初始化程序,并封装到类中
当一个方法声明在以前没有定义的类中,这会比较复杂,当然,调用Method.invoke()方法比直接调用个这个方法要慢得多,这些问题可以通过使用包装类来缓解
使用包装类
这个方法是创建一个类来封装所有新的或者已经存在的类暴露的API,每个包装类中的方法是用过其他方式返回真实方法的相同结果。
如果目标类和方法存在,可以同样的通过这个方法来获取到,但是需要付出比直接调用更多的资源来完成调用,如果目标类和方法不存在,包装类的初始化程序会失败。
想象一下添加了下面这个新类:
public class NewClass { private static int mDiv = 1; private int mMult; public static void setGlobalDiv(int div) { mDiv = div; } public NewClass(int mult) { mMult = mult; } public int doStuff(int val) { return (val * mMult) / mDiv; } }为它创建一个包装类:class WrapNewClass { private NewClass mInstance; /* class initialization fails when this throws an exception */ static { try { Class.forName("NewClass"); } catch (Exception ex) { throw new RuntimeException(ex); } } /* calling here forces class initialization */ public static void checkAvailable() {} public static void setGlobalDiv(int div) { NewClass.setGlobalDiv(div); } public WrapNewClass(int mult) { mInstance = new NewClass(mult); } public int doStuff(int val) { return mInstance.doStuff(val); } }
在包装类中有静态初始化程序测试原来每一个构造函数和方法,如果NewClass不存在,WrapNewClass会初始化失败,确保包装类不被随意使用,方法checkAcailable是使类初始化的一种方法,这样使用它:
public class MyApp { private static boolean mNewClassAvailable; /* establish whether the "new" class is available to us */ static { try { WrapNewClass.checkAvailable(); mNewClassAvailable = true; } catch (Throwable t) { mNewClassAvailable = false; } } public void diddle() { if (mNewClassAvailable) { WrapNewClass.setGlobalDiv(4); WrapNewClass wnc = new WrapNewClass(40); System.out.println("newer API is available - " + wnc.doStuff(10)); } else { System.out.println("newer API not available"); } } }根据经验,如果成功调用checkAvailable,就知道系统中存在NewClass,反之不存在。需要注意的是字节码校检器有不会接受类中有不存在的类的引用,如果存在,调用也会失败,The way this code is structured, the end result is the same whether the exception comes from the verifier or from the call toClass.forName
.有个新方法包装一个已经存在的类,只需要在包装类中加入一个新方法直接调用旧方法,WrapNewClass的静态初始化程序会多做一次反射的检查操作测试才是王道必须测试每个android版本都支持你的程序,可以使程序运行在旧版本的android模拟器上,使用不同API的AVD来测试,关于模拟器可以查看in the AVD documentation或者 emulator -help-virtual-device 命令来源:http://truelife.sinaapp.com/?p=87