【Backward Compatibility for Applications】向后兼容程序

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 to Class.forName.
有个新方法包装一个已经存在的类,只需要在包装类中加入一个新方法直接调用旧方法,WrapNewClass的静态初始化程序会多做一次反射的检查操作
测试才是王道
必须测试每个android版本都支持你的程序,可以使程序运行在旧版本的android模拟器上,使用不同API的AVD来测试,关于模拟器可以查看in the AVD documentation或者 emulator -help-virtual-device 命令
来源:http://truelife.sinaapp.com/?p=87
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值