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的方法需要:

用@TargeApi($API_LEVEL) 使可以編譯通過, 不建議使用@SuppressLint("NewApi");
運行時判斷API level; 僅在足夠高,有此方法的API level系統中,調用此方法;
保證功能完整性,保證低API版本通過其他方法提供功能實現。

 


免責聲明!

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



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