在全世界,现在人们手里有着各种各样的基于Android的设备。而这些设备中,有很多种Android平台的版本在使用,一些运行着最新版平台,而另一些还在运行着老的版本。作为一名开发人员,你需要考虑你的应用程序是否支持后向兼容——你想你的应用程序能在所有的设备上运行吗,或是只是在最新的平台上运行?在某些情况下,在支持的设备上部署新的API,并支持老的设备是很有用的。 可以设置minSdkVersion:

复制代码 然而,如果你想添加一个有用的但不是必须的特性时,例如在硬件键盘可用的时候弹出一个屏幕键盘,你可以这样书写你的代码:允许你的程序使用新的特征,而在老的设备上不会失败

使用反射

假设你想使用android.os.Debug.dumpHprofData(String filename)这个新的方法。Debug这个类自从Android 1.0的时候就已经存在了,但这个方法在Android 1.5(API等级3)中才新增的。如果你想直接调用它,那么,在Android 1.1或更早的设备上,你的app将运行失败。

最简单的方式是通过反射的方式来调用这个方法。这需要做一次查找并在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还是在新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);

}

}

复制代码

包装类拥有和原始类一模一样的方法和构造函数,加上一个静态的初始化方法和测试方法来检查新类是否存在。如果新类不可获得的话,WrapNewClass的初始化会失败,因此,要确保包装类在这种情况下不要被使用。checkAvailable方法是一种强制类进行初始化的简单方法。我们可以像这样来使用:

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成功,我们知道新的类是系统的一部分。如果它失败了,我们知道新的类不存在,并作相应的调整。应该指出的是,由于字节码校验不支持对一个不存在的类的引用,因此,在调用checkAvailable之前就有可能失败。像实例代码那样构建,结果是一样的,可能字节码校验抛出异常或者Class.forName的调用抛出异常。

当包装一个有新方法的已存类时,你只需要在包装类中添加新的方法。老的方法直接调用。新的方法需要在WrapNewClass的静态初始化方法中作一次反射检查。

测试是关键

你必须测试任何想支持的Android框架版本。一般来说,应用程序在不同的版本上行为不同。记住一条法则:如果你不尝试,它就不能工作。 你可以在老版本平台的模拟器上运行应用程序来测试程序的后向兼容性。由于可以创建不同API等级的“虚拟设备”,因此,你可以很容易地进行测试。一旦你创建了AVD,你就可以在新老版本系统上进行程序测试,也许你还可以一边测试一边观察它们的不同点。

android兼容低版本方法,Android 应用程序向低版本兼容的问题相关推荐

  1. android颜色值的表示方法android:background=#FFFFFFFF的意思

    android颜色值的表示方法 android:background="#FFFFFFFF"的意思 Android中的颜色值是通过红(Red).绿(Green).蓝(Blue)三原 ...

  2. android 矢量图片使用方法,Android中的矢量图

    概述 VectorDrawable是通过XML文件中的一系列点,线和曲线及其相关颜色信息定义的. 使用VectorDrawable的主要优点是图像可扩展性. 它可以缩放而不损耗显示质量,这意味着相同的 ...

  3. android studio 优化提速方法,Android Studio速度慢(如何加速)?

    我最近从Eclipse升级到Android Studio,但我并不喜欢这种体验. 我将它们与具有16GB内存的Windows 7 64位旗舰版和运行NVidia Geforce 780的Intel i ...

  4. android中的add方法,Android中Fragment怎么addView?

    慕勒3428872 Fragment是Android honeycomb 3.0新增的概念,在Android--Fragment介绍.AndroidFragment使用.Android Fragmen ...

  5. android的反调试方法,Android平台融合多特征的APP反调试方法与流程

    本发明涉及Android平台融合多特征的APP反调试方法,属于计算机与信息科学技术领域. 背景技术: 应用程序本身并不具备反调试的功能,但是动态调试是动态分析应用逻辑.动态脱壳等攻击方式所采取的必要手 ...

  6. android 开启子线程方法,android中开启子线程

    AndroidRuntime(673): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example/ ...

  7. android横竖屏切换方法,Android横竖屏切换的生命周期

    关于Android手机横竖屏切换时Activity的生命周期问题,网上有很多相似的文章,大多数都是说明在竖屏切换横屏时Activity会重启一次,而在横屏切换竖屏时Activity会重启两次. 我本身 ...

  8. Android解析xml的方法,Android中解析XML格式数据的方法

    XML介绍:Extensible Markup Language,即可扩展标记语言 一.概述 Android中解析XML格式数据大致有三种方法: SAX DOM PULL 二.详解 2.1 SAX S ...

  9. android toolbar的使用方法,Android中Toolbar的基本使用

    Android的标题栏是很重要的一个模块,App是否易用很大一部分要看标题栏.写这个博客的时候刚发现谷歌推出了一种新的标题栏实现方式. 它相对于以前的ActionBar来说,最大的变化是开发者可以在标 ...

  10. android 设置全屏方法,Android中设置全屏的方法

    在开发中,我们经常需要把我们的应用设置为全屏,这里有两种方式: 一是在代码中设置; 二是在配置文件中设置 一. 在代码中设置 public class BaseActivity extends Act ...

最新文章

  1. 哈希--直接定值法和除留取余法
  2. HDU 3507:Print Article
  3. Android 操作串口 (android serial port api)
  4. JavaScript 调试建议和技巧
  5. $\mathfrak {reputation}$
  6. bootstrap加载mysql数据库_bootstrap后台管理系统前后台实现(含数据库)
  7. 【BZOJ1452】【JSOI2009】count
  8. docker容器启动与停止命令
  9. 拆分php中 $i++ ++$i PHP中的i++与++i的区别及效率
  10. 浏览器插件开发--获取淘宝的品牌类型
  11. 搭建excel在线编辑服务器,网站如何实现在线编辑Excel?
  12. 西门子梯形图转换C语言,梯形图和指令表相互转换
  13. arcgis打开Excel文件显示没有注册类的解决方案
  14. FI--SAP财务系统总账应用技巧
  15. 打开网站报数据库错误 is marked as crashed and should be repaired (搞定)
  16. SIM900A保姆级调试日记
  17. SSM出租车查询系统 毕业设计-附源码220915
  18. 文件 服务器属性,去除服务器文件上的RHS属性
  19. 身体和灵魂,总得有一个在路上
  20. SGU 187.Twist and whirl - want to cheat( splay )

热门文章

  1. 开源OA:O2OA平台手机APP指纹认证的配置
  2. 6.Celeste Headlee: 10 ways to have a better conversation | TED Talk
  3. 02计算机优秀毕业论文-摘要·前言
  4. java opennlp_如何在Java中使用OpenNLP?
  5. 午间一乐:no zuo no die,唱起来
  6. 基于加速度计与气压计的三阶卡尔曼滤波计算加速度、速度及高度
  7. C#使用NPOI的方式操作Excel复制行
  8. Boost.Asio Library
  9. 印度影星沙鲁克-罕简介
  10. 旅游四天,吃了一顿七千的饭,坐了一趟一万多的地铁,心疼肾更疼