首页 技术 正文
技术 2022年11月14日
0 收藏 481 点赞 2,579 浏览 5722 个字

在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 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

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,497
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,910
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,744
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,498
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,136
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,300