Android应用在不同版本间兼容性处理
在Android系統(tǒng)中向下兼容性比較差,但是一個(gè)應(yīng)用APP經(jīng)過(guò)處理還是可以在各個(gè)版本間運(yùn)行的。向下兼容性不好,不同版本的系統(tǒng)其API版本也不同,自然有些接口也不同,新的平臺(tái)不能使用舊的API,舊的平臺(tái)也使用不了新的API。
??????? 為了應(yīng)用APP有更好的兼容性,咱們可以利用高版本的SDK開(kāi)發(fā)應(yīng)用,并在程序運(yùn)行時(shí)(Runtime)對(duì)應(yīng)用所運(yùn)行的平臺(tái)判斷,舊平臺(tái)使用舊的API,而新平臺(tái)可使用新的API,這樣可以較好的提高軟件兼容性。
?
??????? 那么,如何在軟件運(yùn)行時(shí)做出這樣的判斷呢?答案下邊揭曉:
?
在Android SDK開(kāi)發(fā)文檔中有段話這樣的話:
Check System Version at Runtime(在軟件運(yùn)行時(shí)檢查判斷系統(tǒng)版本)
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為我們提供了一個(gè)常量類(lèi)Build,其中最主要是Build中的兩個(gè)內(nèi)部類(lèi)VERSION和VERSION_CODES,
VERSION表示當(dāng)前系統(tǒng)版本的信息,其中就包括SDK的版本信息,用于成員SDK_INT表示;
對(duì)于VERSION_CODES在SDK開(kāi)發(fā)文檔中時(shí)這樣描述的,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.
其成員就是一些從最早版本開(kāi)始到當(dāng)前運(yùn)行的系統(tǒng)的一些版本號(hào)常量。
在我們自己開(kāi)發(fā)應(yīng)用過(guò)程中,常常使用如下的代碼形式判斷運(yùn)行新API還是舊的API:
?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { // 包含新API的代碼塊 } else { // 包含舊的API的代碼塊 }???? OK,大家都知道原理了吧! 需要實(shí)例的百度蠻多的,這里就不提供了。
?
?
?
android 10(2.2.3/2.2.4)及以下的版本是沒(méi)有fragment的,從 11(3.0.x) 就有了,這就是新特性,諸如此類(lèi)的還有很多呢。
不明白題主的“向下兼容”具體指哪方面,就我理解的來(lái)說(shuō)吧:
為了使老版本的sdk能用上新版本的特性和功能,官方都會(huì)給出額外的jar包,還是以 fragment 為例,如果我開(kāi)發(fā)的app必須要能在 2.3的系統(tǒng)上運(yùn)行,但同時(shí)要使用 fragment 怎么辦呢?此時(shí)就可以用引入android.support.v4.jar包,這就是官方給的兼容性解決方案了。
可以發(fā)現(xiàn),隨著 SDK 版本的不斷升級(jí),官方給出的jar包也越來(lái)越多,android.support.v7.jar,v13......
如果你想詳細(xì)了解下某些版本的升級(jí)帶來(lái)了哪些新特性,歡迎訪問(wèn)Android 5.0 Behavior Changes,當(dāng)然,感興趣的話也可以找到歷史版本的升級(jí)記錄,在這里就不多說(shuō)了。。。
?
?
?
Android 版本更替,新的版本帶來(lái)新的特性,新的方法。
新的方法帶來(lái)許多便利,但無(wú)法在低版本系統(tǒng)上運(yùn)行,如果兼容性處理不恰當(dāng),APP在低版本系統(tǒng)上,運(yùn)行時(shí)將會(huì)crash。
本文以一個(gè)具體的例子說(shuō)明如何在使用高API level的方法時(shí)處理好兼容性問(wèn)題。
例子:根據(jù)給出路徑,獲取此路徑所在分區(qū)的總空間大小。
在安卓中的文件存儲(chǔ)使用參考中提到:
獲取文件系統(tǒng)用量情況,在API level 9及其以上的系統(tǒng),可直接調(diào)用File對(duì)象的相關(guān)方法,以下需自行計(jì)算
一般實(shí)現(xiàn)
就此需求而言,API level 9及其以上,調(diào)用?File.getTotalSpace()?即可, 但是在API level 8 以下系統(tǒng)File對(duì)象并不存在此方法。
如以下方法:
/*** 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(); }處理無(wú)法編譯通過(guò)
如果minSdkVersion設(shè)置為8,那么build時(shí)候會(huì)報(bào)以下錯(cuò)誤:
Call requires API level 9 (current min is 8)為了編譯可以通過(guò),可以添加?@SuppressLint("NewApi")?或者?@TargeApi(9)。
用@TargeApi($API_LEVEL)顯式表明方法的API level要求,而不是@SuppressLint("NewApi");
但是這樣只是能編譯通過(guò),到了API level8的系統(tǒng)運(yùn)行,將會(huì)引發(fā)?java.lang.NoSuchMethodError。
正確的做法
為了運(yùn)行時(shí)不報(bào)錯(cuò), 需要:
同時(shí)為了保證功能的完整性,需要提供低版本功能實(shí)現(xiàn)
如下:
/*** 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 GINGERBREADelse {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 warningreturn (long) stats.getBlockSize() * (long) stats.getBlockCount();}} }總結(jié)
在使用高于minSdkVersion?API level的方法需要:
?
?
?
Android 開(kāi)發(fā)之API兼容問(wèn)題
問(wèn)題背景
鑒于ANDROID SDK 更新較快,很多新的特性和API在低版本中的可能沒(méi)有。所以開(kāi)發(fā)過(guò)程中盡量要保持對(duì)新功能接口的兼容。
一般開(kāi)發(fā)過(guò)程中APP都會(huì)有一個(gè)最低版本的配置,例如如果要兼容到android 2.2系統(tǒng),則可以設(shè)置minSdkVersion=8,這就表明能向下兼容到android 2.2版本,即APP能在android2.2版本上的手機(jī)也能正常運(yùn)行,即使可能某些新特性的功能支持失效,但至少保證不會(huì)出現(xiàn)崩潰的問(wèn)題,而避免此問(wèn)題的方式就要求開(kāi)發(fā)者在代碼中做好兼容和適配。
?
兼容原則
一般選擇APP的最低支持版本原則是盡量向下保持兼容,但也不是說(shuō)越向下越好,主要的考慮因素有以下幾點(diǎn):
1.??????各個(gè)低版本手機(jī)的市場(chǎng)占有率,比如2013年android 2.2的手機(jī)還占用一定的市場(chǎng)份額,但到現(xiàn)在為止基本上該份額可以忽略不計(jì)了(目前android 最高的版本已達(dá)到android 5.1了)
2.??????APP的針對(duì)用戶群體,比如是高端的用戶群體,屌絲用戶群體,還是中低端用戶群體,根據(jù)不同的用戶群體可以綜合出來(lái)決定對(duì)最低版本的支持。
基于SDK高低開(kāi)發(fā)優(yōu)缺點(diǎn)
基于低版本的SDK開(kāi)發(fā)
優(yōu)點(diǎn)就是你可以支持的手機(jī)用戶會(huì)更多,基本上各個(gè)版本的用戶都可以用你的應(yīng)用。
但缺點(diǎn)也是非常明顯,特別是對(duì)開(kāi)發(fā)者來(lái)說(shuō),需要做好每一個(gè)新特性功能的適配和開(kāi)發(fā),隨著版本越來(lái)越高,這對(duì)開(kāi)發(fā)者后期的維護(hù)會(huì)越來(lái)越困難,越來(lái)越多。
基于高版本的SDK開(kāi)發(fā)
如果你用最新的版本的SDK, 優(yōu)點(diǎn)就是你可以使用最新的功能的api,而且編譯也不會(huì)出現(xiàn)任何問(wèn)題。
但是缺點(diǎn)就是你需要時(shí)刻對(duì)你調(diào)用的api保持向下兼容性,因?yàn)楹苡锌赡苣悻F(xiàn)有調(diào)用的某個(gè)api在低版本中根本就不存在。這時(shí)候你需要考慮低版本系統(tǒng)的用戶的運(yùn)行問(wèn)題了。
?
?
實(shí)戰(zhàn)分析
如某個(gè)工程配置中的最低版本是android2.2,也就是正常來(lái)說(shuō)開(kāi)發(fā)過(guò)程中需要基于android SDK為8來(lái)做工程開(kāi)發(fā)。但如果你沒(méi)有基于adroid? 2.2 SDK版本開(kāi)發(fā),而是支持了一個(gè)更高的版本,比如android 4.0 SDK開(kāi)發(fā),那么很多高版本的功能特性(2.3—4.0)在4.0以下的手機(jī)中運(yùn)行就可以存在問(wèn)題,一般的結(jié)果就是直接crash。
下面是基于android2.2 SDK 開(kāi)發(fā)環(huán)境編譯的最新的工程,其中就有一些直接編譯運(yùn)行不過(guò)的錯(cuò)誤。下面可以看幾個(gè)實(shí)例:
SampleActivity.java有一處這樣寫(xiě)的:
??????if?(savedInstanceState !=null) {
?????????mOrderId?=savedInstanceState.getString(EXTRA_ORDER_ID);
?????????mPaySuccess?=savedInstanceState.getString(EXTRA_PAY_SUCCESS,"");
????? }
代碼中使用Bundle對(duì)象在新版本中才提供的方法而沒(méi)有加兼容處理,如下官方文檔中解釋,該方法在android 3.1后才有。
public?String?getString?(String?key,?String?defaultValue)?Added in?API level 12
如果在低于android 3.0下機(jī)器運(yùn)行和編譯該代碼,如果不做任何處理,會(huì)直接編譯通不過(guò)。
?
解決方法:
1.?????? 用android提供的注解 @TargetApi(11)+ 版本號(hào)控制做兼容
如果是基于高版本的SDK開(kāi)發(fā),則新的api肯定會(huì)有該方法,如果想讓編譯的版本在低版本中也能運(yùn)行,則需要考慮到版本兼容的問(wèn)題,可以用如下的方式:
/***
???? *?該api版本兼容獲取指定參數(shù)
???? *
???? *?@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.?????? 用反射的方式調(diào)用高版本中的新功能接口進(jìn)行調(diào)用。
如果是基于低版本SDK開(kāi)發(fā),那么新版本中的新接口肯定會(huì)編譯不過(guò),這時(shí)候可以考慮反射的方式先去查找是否存在這個(gè)方法,如果有就代表用戶的手機(jī)支持該調(diào)用方法,如果沒(méi)有則采用低版本的處理方式。
?
???/***
???? *?通過(guò)放射的方式來(lái)獲取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上編譯運(yùn)行,最后ClassLoader動(dòng)態(tài)加載高版本中的相關(guān)類(lèi)接口
此方法應(yīng)用場(chǎng)景如2,可以將高版本的api接口封裝后在高版本的SDK中編譯運(yùn)行jar包,供舊版本的工程中動(dòng)態(tài)加載。
SDK相關(guān)對(duì)應(yīng)表
| 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 | 10 | GINGERBREAD_MR1 | Platform Highlights |
| Android 2.3.2 | 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
轉(zhuǎn)載于:https://www.cnblogs.com/yaowen/p/5013366.html
總結(jié)
以上是生活随笔為你收集整理的Android应用在不同版本间兼容性处理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Calico在Docker中的搭建
- 下一篇: VFL语言的使用