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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM