Android应用在不同版本间兼容性处理

在Android系统中向下兼容性比较差,但是一个应用APP经过处理还是可以在各个版本间运行的。向下兼容性不好,不同版本的系统其API版本也不同,自然有些接口也不同,新的平台不能使用旧的API,旧的平台也使用不了新的API。

为了应用APP有更好的兼容性,咱们可以利用高版本的SDK开发应用,并在程序运行时(Runtime)对应用所运行的平台判断,旧平台使用旧的API,而新平台可使用新的API,这样可以较好的提高软件兼容性。

那么,如何在软件运行时做出这样的判断呢?答案下边揭晓:

  在Android SDK开发文档中有段话这样的话:

Check System Version at Runtime(在软件运行时检查判断系统版本)



Android provides a unique code for each platform version in the Build constants class. Use these codes within your app to build conditions that ensure the code thatdepends on higher API levels is executed only when those APIs are available on the system.

private void setUpActionBar() {
    // Make sure we‘re running on Honeycomb or higher to use ActionBar APIs
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {

         ActionBar actionBar = getActionBar();
         actionBar.setDisplayHomeAsUpEnabled(true);
    }
}

Note: When parsing XML resources, Android ignores XML attributes that aren’t supported by the current device. So you can safely use XML attributes thatare only supported by newer versions without worrying about older versions breaking when theyencounter that code. For example, if you set the targetSdkVersion="11", your app includes the ActionBar by defaulton Android 3.0 and higher. To then add menu items to the action bar, you need to set android:showAsAction="ifRoom" in your menu resource XML. It‘s safe to do this in a cross-version XML file, because the older versions of Android simply ignore the showAsAction attribute (that is, you do not need a separate version in res/menu-v11/).

从上面可以知道Android为我们提供了一个常量类Build,其中最主要是Build中的两个内部类VERSION和VERSION_CODES,

VERSION表示当前系统版本的信息,其中就包括SDK的版本信息,用于成员SDK_INT表示;

对于VERSION_CODES在SDK开发文档中时这样描述的,Enumeration of the currently known SDK version codes. These are the values that can be found in SDK. Version numbers increment monotonically with each official platform release.

其成员就是一些从最早版本开始到当前运行的系统的一些版本号常量。

  在我们自己开发应用过程中,常常使用如下的代码形式判断运行新API还是旧的API:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
    {
            // 包含新API的代码块
    }
    else
    {
            // 包含旧的API的代码块
    }

OK,大家都知道原理了吧! 需要实例的百度蛮多的,这里就不提供了。

android 10(2.2.3/2.2.4)及以下的版本是没有fragment的,从 11(3.0.x) 就有了,这就是新特性,诸如此类的还有很多呢。

不明白题主的“向下兼容”具体指哪方面,就我理解的来说吧:

为了使老版本的sdk能用上新版本的特性和功能,官方都会给出额外的jar包,还是以 fragment 为例,如果我开发的app必须要能在 2.3的系统上运行,但同时要使用 fragment 怎么办呢?此时就可以用引入android.support.v4.jar包,这就是官方给的兼容性解决方案了。

可以发现,随着 SDK 版本的不断升级,官方给出的jar包也越来越多,android.support.v7.jar,v13......

如果你想详细了解下某些版本的升级带来了哪些新特性,欢迎访问Android 5.0 Behavior Changes,当然,感兴趣的话也可以找到历史版本的升级记录,在这里就不多说了。。。

Android 版本更替,新的版本带来新的特性,新的方法。

新的方法带来许多便利,但无法在低版本系统上运行,如果兼容性处理不恰当,APP在低版本系统上,运行时将会crash。

本文以一个具体的例子说明如何在使用高API level的方法时处理好兼容性问题。

例子:根据给出路径,获取此路径所在分区的总空间大小。

安卓中的文件存储使用参考中提到:

获取文件系统用量情况,在API level 9及其以上的系统,可直接调用File对象的相关方法,以下需自行计算

一般实现

就此需求而言,API level 9及其以上,调用 File.getTotalSpace() 即可, 但是在API level 8 以下系统File对象并不存在此方法。

如以下方法:

/**
 * Returns the total size in bytes of the partition containing this path.
 * Returns 0 if this path does not exist.
 *
 * @param path
 * @return -1 means path is null, 0 means path is not exist.
 */
public static long getTotalSpace(File path) {
    if (path == null) {
        return -1;
    }
    return path.getTotalSpace();
}
处理无法编译通过

如果minSdkVersion设置为8,那么build时候会报以下错误:

Call requires API level 9 (current min is 8)

为了编译可以通过,可以添加 @SuppressLint("NewApi") 或者 @TargeApi(9)

@TargeApi($API_LEVEL)显式表明方法的API level要求,而不是@SuppressLint("NewApi");

但是这样只是能编译通过,到了API level8的系统运行,将会引发 java.lang.NoSuchMethodError

正确的做法

为了运行时不报错, 需要:

  1. 判断运行时版本,在低版本系统不调用此方法
  2. 同时为了保证功能的完整性,需要提供低版本功能实现

    如下:

    /**
     * Returns the total size in bytes of the partition containing this path.
     * Returns 0 if this path does not exist.
     *
     * @param path
     * @return -1 means path is null, 0 means path is not exist.
     */
    @TargetApi(Build.VERSION_CODES.GINGERBREAD)
        // using @TargeApi instead of @SuppressLint("NewApi")
    @SuppressWarnings("deprecation")
    public static long getTotalSpace(File path) {
        if (path == null) {
            return -1;
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
            return path.getTotalSpace();
        }
        // implements getTotalSpace() in API lower than GINGERBREAD
        else {
            if (!path.exists()) {
                return 0;
            } else {
                final StatFs stats = new StatFs(path.getPath());
                // Using deprecated method in low API level system,
                // add @SuppressWarnings("description") to suppress the warning
                return (long) stats.getBlockSize() * (long) stats.getBlockCount();
            }
        }
    }
    

总结

在使用高于minSdkVersion API level的方法需要:

  1. @TargeApi($API_LEVEL) 使可以编译通过, 不建议使用@SuppressLint("NewApi");
  2. 运行时判断API level; 仅在足够高,有此方法的API level系统中,调用此方法;
  3. 保证功能完整性,保证低API版本通过其他方法提供功能实现。

Android 开发之API兼容问题

问题背景

鉴于ANDROID SDK 更新较快,很多新的特性和API在低版本中的可能没有。所以开发过程中尽量要保持对新功能接口的兼容。

一般开发过程中APP都会有一个最低版本的配置,例如如果要兼容到android 2.2系统,则可以设置minSdkVersion=8,这就表明能向下兼容到android 2.2版本,即APP能在android2.2版本上的手机也能正常运行,即使可能某些新特性的功能支持失效,但至少保证不会出现崩溃的问题,而避免此问题的方式就要求开发者在代码中做好兼容和适配。

兼容原则

一般选择APP的最低支持版本原则是尽量向下保持兼容,但也不是说越向下越好,主要的考虑因素有以下几点:

1.      各个低版本手机的市场占有率,比如2013年android 2.2的手机还占用一定的市场份额,但到现在为止基本上该份额可以忽略不计了(目前android 最高的版本已达到android 5.1了)

2.      APP的针对用户群体,比如是高端的用户群体,屌丝用户群体,还是中低端用户群体,根据不同的用户群体可以综合出来决定对最低版本的支持。

基于SDK高低开发优缺点

基于低版本的SDK开发

优点就是你可以支持的手机用户会更多,基本上各个版本的用户都可以用你的应用。

但缺点也是非常明显,特别是对开发者来说,需要做好每一个新特性功能的适配和开发,随着版本越来越高,这对开发者后期的维护会越来越困难,越来越多。

基于高版本的SDK开发

如果你用最新的版本的SDK, 优点就是你可以使用最新的功能的api,而且编译也不会出现任何问题。

但是缺点就是你需要时刻对你调用的api保持向下兼容性,因为很有可能你现有调用的某个api在低版本中根本就不存在。这时候你需要考虑低版本系统的用户的运行问题了。

实战分析

如某个工程配置中的最低版本是android2.2,也就是正常来说开发过程中需要基于android SDK为8来做工程开发。但如果你没有基于adroid  2.2 SDK版本开发,而是支持了一个更高的版本,比如android 4.0 SDK开发,那么很多高版本的功能特性(2.3—4.0)在4.0以下的手机中运行就可以存在问题,一般的结果就是直接crash。

下面是基于android2.2 SDK 开发环境编译的最新的工程,其中就有一些直接编译运行不过的错误。下面可以看几个实例:

SampleActivity.java有一处这样写的:

if (savedInstanceState !=null) {

mOrderId =savedInstanceState.getString(EXTRA_ORDER_ID);

mPaySuccess =savedInstanceState.getString(EXTRA_PAY_SUCCESS,"");

}

代码中使用Bundle对象在新版本中才提供的方法而没有加兼容处理,如下官方文档中解释,该方法在android 3.1后才有。

public String getString (String key, String defaultValue) Added in API level 12

如果在低于android 3.0下机器运行和编译该代码,如果不做任何处理,会直接编译通不过。

解决方法:

1.       用android提供的注解 @TargetApi(11)+ 版本号控制做兼容

如果是基于高版本的SDK开发,则新的api肯定会有该方法,如果想让编译的版本在低版本中也能运行,则需要考虑到版本兼容的问题,可以用如下的方式:

/***

* 该api版本兼容获取指定参数

*

@param savedInstanceState

@return

*/

@TargetApi(12)

privateString getPaySucess(Bundle savedInstanceState) {

if (Build.VERSION.SDK_INT >= 12) {

mPaySuccess = savedInstanceState.getString(EXTRA_PAY_SUCCESS,"");

else {

mPaySuccess = savedInstanceState.getString(EXTRA_PAY_SUCCESS);

if (mPaySuccess ==null){

mPaySuccess = "";

}

}

returnmPaySuccess;

}

2.       用反射的方式调用高版本中的新功能接口进行调用。

如果是基于低版本SDK开发,那么新版本中的新接口肯定会编译不过,这时候可以考虑反射的方式先去查找是否存在这个方法,如果有就代表用户的手机支持该调用方法,如果没有则采用低版本的处理方式。

 

/***

* 通过放射的方式来获取Bundle中的

* getString(String key,String value)方法

*

@return

*/

privateStringgetPaySucessInvoke(Bundle savedInstanceState) {

try {

Class<?> c = Class.forName("android.os.bundle");

Method mGetString2Params =c.getDeclaredMethod("getString", String.class,String.class);

if (mGetString2Params !=null) {

mPaySuccess = (String)mGetString2Params.invoke(null,EXTRA_PAY_SUCCESS,"");

else {

mPaySuccess = savedInstanceState.getString(EXTRA_PAY_SUCCESS);

if (mPaySuccess ==null){

mPaySuccess ="";

}

}

catch (Exception e) {

// TODO: handle exception

}

returnmPaySuccess;

}

3.       分离代码,分别在不同的SDK上编译运行,最后ClassLoader动态加载高版本中的相关类接口

此方法应用场景如2,可以将高版本的api接口封装后在高版本的SDK中编译运行jar包,供旧版本的工程中动态加载。

SDK相关对应表


Platform Version


API Level


VERSION_CODE


Notes


Android 5.1


22


LOLLIPOP_MR1


Platform Highlights


Android 5.0


21


LOLLIPOP


Android 4.4W


20


KITKAT_WATCH


KitKat for Wearables Only


Android 4.4


19


KITKAT


Platform Highlights


Android 4.3


18


JELLY_BEAN_MR2


Platform Highlights


Android 4.2, 4.2.2


17


JELLY_BEAN_MR1


Platform Highlights


Android 4.1, 4.1.1


16


JELLY_BEAN


Platform Highlights


Android 4.0.3, 4.0.4


15


ICE_CREAM_SANDWICH_MR1


Platform Highlights


Android 4.0, 4.0.1, 4.0.2


14


ICE_CREAM_SANDWICH


Android 3.2


13


HONEYCOMB_MR2


Android 3.1.x


12


HONEYCOMB_MR1


Platform Highlights


Android 3.0.x


11


HONEYCOMB


Platform Highlights


Android 2.3.4
Android 2.3.3


10


GINGERBREAD_MR1


Platform Highlights


Android 2.3.2
Android 2.3.1
Android 2.3


9


GINGERBREAD


Android 2.2.x


8


FROYO


Platform Highlights


Android 2.1.x


7


ECLAIR_MR1


Platform Highlights


Android 2.0.1


6


ECLAIR_0_1


Android 2.0


5


ECLAIR


Android 1.6


4


DONUT


Platform Highlights


Android 1.5


3


CUPCAKE


Platform Highlights


Android 1.1


2


BASE_1_1


Android 1.0


1


BASE

参考:

http://developer.android.com/reference/packages.html

时间: 2024-10-13 03:28:40

Android应用在不同版本间兼容性处理的相关文章

android应用的不同版本间兼容性处理

在Android系统中向下兼容性比较差,但是一个应用APP经过处理还是可以在各个版本间运行的.向下兼容性不好,不同版本的系统其API版本也不同,自然有些接口也不同,新的平台不能使用旧的API,旧的平台也使用不了新的API. 为了应用APP有更好的兼容性,咱们可以利用高版本的SDK开发应用,并在程序运行时(Runtime)对应用所运行的平台判断,旧平台使用旧的API,而新平台可使用新的API,这样可以较好的提高软件兼容性. 那么,如何在软件运行时做出这样的判断呢?答案下边揭晓: 在Android

jquery版本间兼容性:checkbox选中状态

最近在学习Jquery,发现attr不太好用,从网上搜了下终于知道其原因,记下备查. 以下为以为网友在js贴吧的内容: 以<input type="checkbox" id="all"/>为例子取值的例子$("#all").attr("checked")在1.6前(含1.6),返回值是boolean类型的true或false:在1.6后,返回值是"checked"或undefined. 再来看赋

Android sdk版本以及兼容性问题

Android:minSdkVersion —— 此属性决定你的应用能兼容的最低的系统版本,一盘情况是必须设置此属性. android:targetSdkVersion —— 此属性说明你当前的应用是针对某一个系统版本开发设计的,也就是说在这个系统版本上运行是没有任何问题的.对于手机或其他终端设备会根据此属性值,决定是否显示一些特性和效果.当然对于开发者最直接的影响就是,你所用到的API都是基于此版本上的,高于此targetSdkVersion的API在使用的时候会有警告或者错误提示. andr

Android API在不同版本系统上的兼容性

随着安卓版本的不断更新,新的API不断涌出,有时候高版本的API会在低版本crash的. 如果minSdkVersion设置过低,在build的时候,就会报错(Call requires API level 17 (current min is 9)): 这时候为了编译可以通过,可以添加 @SuppressLint("NewApi") 或者 @TargeApi(9)注解. 这样只是编译通过,运行时依然会crash的. 正确的做法 为了运行时不报错, 需要: 判断运行时版本,在低版本系统

private static final long serialVersionUID = 1L用来表明类的不同版本间的兼容性

Java中serialVersionUID的解释 serialVersionUID作用: 相当于java类的身份证.主要用于版本控制.serialVersionUID作用是序列化时保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性. 序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性.有两种生成方式: 一个是默认的1L,比如:private static final long serialVersionUID = 1L; 一个是根据类名.接口名.成员方法及属性等来生成

Android开发之Tween(补间动画)完全解析(下)

欢迎转载,转载请注明出处:http://blog.csdn.net/dmk877/article/details/51980734 在上一篇文章中,我们详细讨论了Tween动画的xml的实现以及interpolator的使用,相信通过上篇文章大家对Tween动画的xml属性的配置会有一个详细的理解,当然这篇文章也是承接上篇文章,所以强烈建议先阅读上篇文章:Android开发之Tween(补间动画)完全解析(上),这篇文章将从代码的角度实现上篇文章的效果.如有疑问请留言,如有谬误欢迎批评指正. T

【原创】Android 4.4前后版本读取图库图片方式的变化

Android 4.4前后版本读取图库图片方式的变化 本文讲述Android 4.4(KitKat)前后访问图库以及访问后通过图片路径读取图片的变化 Android 4.4(KitKat)以前: 访问图库(方法一): 1 /** 2 * Access the gallery to pick up an image. 3 */ 4 private void startPickPhotoActivity() { 5 Intent intent = new Intent(Intent. ACTION_

Android 使用handler实现线程间发送消息 (主线程 与 子线程之间)、(子线程 与 子线程之间)

keyword:Android 使用handler实现线程间发送消息 (主线程 与 子线程之间).(子线程 与 子线程之间) 相信大家平时都有使用到异步线程往主线程(UI线程)发送消息的情况. 本文主要研究Handler的消息发送. 包含主线程往子线程发送消息,子线程之间互相发送消息. 一.主线程向子线程发送消息. 实现过程比較简单: 主线程发送消息到异步线程.异步线程接收到消息后在再发送一条消息给主线程. 1. 初始化主线程的Handler,用来接收子线程的消息. 2. 启动异步线程.在异步线

窥探Swift系列博客说明及其Swift版本间更新

Swift到目前为止仍在更新,每次更新都会推陈出新,一些Swift旧版本中的东西在新Swift中并不适用,而且新版本的Swift会添加新的功能.到目前为止,Swift为2.1版本.去年翻译的Swift书籍是1.0版本,所以上面一些东西并不在适用.虽然Swift语言仍在更新,但是其整体的基础框架已经形成,大的改动应该不会有,版本的更新更多的是语言新功能的添加和完善,所以并不用担心现在学的Swift会过时.更新也就是在原有的基础上去更新,所以学学Swift还是很有必要的.新的Swift版本中引入了好