版權(quán)聲明:本文為博主原創(chuàng)文章(部分引用他人博文,已加上引用說明),未經(jīng)博主允許不得轉(zhuǎn)載。http://www.itdecent.cn/p/49fa8ebc0105
轉(zhuǎn)載請(qǐng)標(biāo)明出處:
http://www.itdecent.cn/p/49fa8ebc0105
本文出自 AWeiLoveAndroid的博客
上一篇文章講了 屏幕適配 http://www.itdecent.cn/p/7aa34434ad4d
這一篇文章講一下 版本適配 http://www.itdecent.cn/p/49fa8ebc0105
下一篇文章講一下 ROM適配 http://www.itdecent.cn/p/f9c67a4b908e

在我們的開發(fā)中,會(huì)對(duì)不同安卓版本做適配,比如我之前做過的項(xiàng)目中最低兼容到4.4,最高兼容是最新的系統(tǒng)7.1,由于不同版本的系統(tǒng)中部分API版本也不同,我就要對(duì)這些API做特殊處理。新的平臺(tái)有一些API不能使用舊的API,舊的平臺(tái)也使用不了新的API。所以這就要考驗(yàn)我們開發(fā)人員的能力了。我這里簡(jiǎn)單給出幾點(diǎn)我開發(fā)中使用過的一些方式,僅供參考:
一、同一個(gè)api在不同版本都存在,只是api的一些接口方法有變更。
這種情況是最好處理的,只要對(duì)版本號(hào)做判斷,對(duì)應(yīng)的系統(tǒng)版本用相應(yīng)的api方法就好了。為了好維護(hù),建議做一個(gè)簡(jiǎn)單的封裝。
舉例說明如下:
比如Notification在不同版本的兼容,舉例如下:
首先打開谷歌官方文檔,看看文檔里面的一些說明:
1.Notification這個(gè)類是added in API level 1,一直都有,只是具體某些方法有變更。繼續(xù)往下看。
2.這個(gè)類有個(gè)說明,意思是Notification.Builder是新增的一個(gè)內(nèi)部類,用它創(chuàng)建通知更方便。接著往下看。
A class that represents how a persistent notification is to
be presented to the user using the NotificationManager.
The Notification.Builder has been added to make it easier
to construct Notifications.
3.Public constructors公共的構(gòu)造方法,其中有3個(gè)參數(shù)的這個(gè)在api 11過時(shí),它被Notification.Builder替代了。
Notification(int icon, CharSequence tickerText, long when)
This constructor was deprecated in API level 11.
Use Notification.Builder instead.
4.常量
EXTRA_LARGE_ICONThis constant was deprecated in API level 26. Use getLargeIcon(), which supports a wider variety of icon sources.(在API級(jí)別26中已棄用。使用getLargeIcon(),它支持更多種圖標(biāo)源。)EXTRA_SMALL_ICONThis constant was deprecated in API level 26. Use getSmallIcon(), which supports a wider variety of icon sources.(在API級(jí)別26中已棄用。使用getSmallIcon(),它支持更多種圖標(biāo)源。)FLAG_HIGH_PRIORITYThis constant was deprecated in API level 16. Use priority with a positive value.(在api16被棄用,請(qǐng)使用正數(shù)priority值替代)FLAG_SHOW_LIGHTSThis constant was deprecated in API level 26. use shouldShowLights().(在API級(jí)別26中已棄用。請(qǐng)使用shouldShowLights()替代)PRIORITY_DEFAULTThis constant was deprecated in API level 26. use IMPORTANCE_DEFAULT instead.(在API級(jí)別26中已棄用。請(qǐng)使用IMPORTANCE_DEFAULT替代)PRIORITY_HIGHThis constant was deprecated in API level 26. use IMPORTANCE_HIGH instead.(在API級(jí)別26中已棄用。請(qǐng)使用IMPORTANCE_HIGH替代)PRIORITY_LOWThis constant was deprecated in API level 26. use IMPORTANCE_LOW instead.(在API級(jí)別26中已棄用。請(qǐng)使用IMPORTANCE_LOW替代)PRIORITY_MAXThis constant was deprecated in API level 26. use IMPORTANCE_HIGH instead.(在API級(jí)別26中已棄用。請(qǐng)使用IMPORTANCE_HIGH替代)PRIORITY_MINThis constant was deprecated in API level 26. use IMPORTANCE_MIN instead.(在API級(jí)別26中已棄用。請(qǐng)使用IMPORTANCE_MIN替代)STREAM_DEFAULTThis constant was deprecated in API level 21. Use getAudioAttributes() instead.(在API級(jí)別21中已棄用。請(qǐng)使用getAudioAttributes()替代)
5.字段Fields
audioAttributes在api 26棄用. 使用getAudioAttributes()替代.audioStreamType在api 21棄用. 使用audioAttributes替代.defaults此字段在API 26棄用。使用getSound()和shouldShowLights()和shouldVibrate()。icon此字段已在API級(jí)別26中棄用。使用setSmallIcon(Icon)替代。largeIconThis field was deprecated in API level 23. Use `setLargeIcon(Icon) instead.ledARGBThis field was deprecated in API level 26. use `shouldShowLights().ledOffMSThis field was deprecated in API level 26. use `shouldShowLights().ledOnMSThis field was deprecated in API level 26. useshouldShowLights().priorityThis field was deprecated in API level 26. usegetImportance()instead.soundThis field was deprecated in API level 26. usegetSound()instead.vibrateThis field was deprecated in API level 26. usegetVibrationPattern().
二、Android6.0的動(dòng)態(tài)權(quán)限介紹
因?yàn)锳ndroid6.0(API23)開始需要?jiǎng)討B(tài)申請(qǐng)權(quán)限,需要手動(dòng)申請(qǐng)的權(quán)限有8組(短信、電話、聯(lián)系人、存儲(chǔ)、位置、麥克風(fēng)、日歷、相機(jī)),共24個(gè),如下所示:
| 所屬權(quán)限組 | 權(quán)限 |
|---|---|
| 短信 | SEND_SMS |
| 短信 | RECEIVE_SMS |
| 短信 | READ_SMS |
| 短信 | RECEIVE_WAP_PUSH |
| 短信 | RECEIVE_MMS |
| 電話 | READ_PHONE_STATE |
| 電話 | CALL_PHONE |
| 電話 | READ_CALL_LOG |
| 電話 | WRITE_CALL_LOG |
| 電話 | ADD_VOICEMAIL |
| 電話 | USE_SIP |
| 電話 | PROCESS_OUTGOING_CALLS |
| 聯(lián)系人 | READ_CONTACTS |
| 聯(lián)系人 | WRITE_CONTACTS |
| 聯(lián)系人 | GET_ACCOUNTS |
| 存儲(chǔ) | READ_EXTERNAL_STORAGE |
| 存儲(chǔ) | WRITE_EXTERNAL_STORAGE |
| 位置 | ACCESS_FINE_LOCATION |
| 位置 | ACCESS_COARSE_LOCATION |
| 麥克風(fēng) | RECORD_AUDIO |
| 日歷 | READ_CALENDAR |
| 日歷 | WRITE_CALENDAR |
| 相機(jī) | CAMERA |
| 傳感器 | BODY_SENSORS |
注意:如果應(yīng)用程序請(qǐng)求在AndroidManifest中列出的危險(xiǎn)權(quán)限,并且應(yīng)用程序已經(jīng)在同一權(quán)限組中具有另一個(gè)危險(xiǎn)權(quán)限,系統(tǒng)會(huì)立即授予權(quán)限,而不會(huì)與用戶進(jìn)行任何交互。
例如,如果一個(gè)應(yīng)用程序先前已經(jīng)請(qǐng)求并被授予READ_CONTACTS權(quán)限,然后它請(qǐng)求WRITE_CONTACTS(同屬于聯(lián)系人一組),系統(tǒng)會(huì)立即授予該權(quán)限,不會(huì)再彈出權(quán)限授予詢問的對(duì)話框。
三、Android6.0如何申請(qǐng)動(dòng)態(tài)權(quán)限
開發(fā)中經(jīng)常會(huì)遇到拍照的權(quán)限申請(qǐng),這里就講一下如何動(dòng)態(tài)設(shè)置拍照權(quán)限:
//別忘記在清單文件也加上CAMERA權(quán)限
//<uses-permission android:name="android.permission.CAMERA" />
// 定義識(shí)別碼
public static final int CAMERA_OK = 1;
//動(dòng)態(tài)申請(qǐng)拍照權(quán)限
if (Build.VERSION.SDK_INT>22){
if (ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED){
//先判斷有沒有權(quán)限 ,沒有就在這里進(jìn)行權(quán)限的申請(qǐng)
requestPermissions(new String[]{Manifest.permission.CAMERA}, CAMERA_OK);
}else {
//說明已經(jīng)獲取到攝像頭權(quán)限了,可以去選擇照片或者拍照了。
toSelectPhotoOrOpenCamera();
}
}else {
//這個(gè)說明系統(tǒng)版本在6.0之下,不需要?jiǎng)討B(tài)獲取權(quán)限,直接去選擇照片或者拍照。
toSelectPhotoOrOpenCamera();
}
//在Activity中重寫權(quán)限獲取方法:
/**
* 權(quán)限操作結(jié)果處理
*/
@Override
public void onRequestPermissionsResult(int requestCode,
String[] permissions, int[] grantResults) {
switch (requestCode) {
case CAMERA_OK:
if (grantResults.length > 0 && grantResults[0]
== PackageManager.PERMISSION_GRANTED) {
//用戶已授權(quán)
toSelectPhotoOrOpenCamera();
} else {
//用戶拒絕權(quán)限
ToastUtils.show(this,
"缺少相機(jī)權(quán)限,暫時(shí)無法提供掃描功能,請(qǐng)嘗試在設(shè)置中打開相機(jī)權(quán)限!",
Toast.LENGTH_LONG);
}
break;
}
}
}
四、Android7.0對(duì)文件權(quán)限進(jìn)一步升級(jí),提出了新的類FileProvider來獲取文件。所以適配的時(shí)候一定要注意這一點(diǎn)api的變化。
FileProvider是ContentProvider的子類,把原來文件共享的 file://uri 換成了 content://uri 。一個(gè)Uri允許你獲取臨時(shí)權(quán)限去讀寫文件,當(dāng)使用含有Uri的Intent,可以使用Intent.setFlags來添加臨時(shí)權(quán)限。
下面來看看調(diào)用系統(tǒng)相機(jī)拍攝照片有如何變化,大致步驟如下所示:
(一)在manifest中添加Provider
<manifest>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.lzw.demo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
...
</provider>
...
</application>
</manifest>
(二)配置你要獲取的文件所在的文件夾 --> 創(chuàng)建一個(gè)xml文件,比如file_demo.xml,文件內(nèi)容如下:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
...
</paths>
路徑說明:
<files-path name="name" path="path/" />
<!--等同于Context.getFilesDir()下面的path文件夾的所有文件-->
<cache-path name="name" path="path/" />
<!--等同于Context.getCacheDir()下面的path文件夾-->
<external-path name="name" path="path/" />
<!--等同于Environment.getExternalStorageDirectory()下面的path文件夾-->
<external-files-path name="name" path="path/" />
<!--等同于 Context#getExternalFilesDir(String)下面子文件path文件夾-->
<external-cache-path name="name" path="path/" />
<!--相當(dāng)于 Context.getExternalCacheDir()下邊的path文件夾-->
(三)添加路徑信息到provier
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.lzw.demo.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_demo" />
</provider>
(四)現(xiàn)在可以去拍照了。(由于Android6.0開始要?jiǎng)討B(tài)申請(qǐng)權(quán)限,所以別忘了,這里就不寫了,主要講FileProvider的使用)
//適配7.0的fileprovider,imgfile是圖片文件路徑
public void TakePhotoAdaption(File imgFile){
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//適配android7.0 手機(jī)拍照取uri的處理
if(Build.VERSION.SDK_INT<24){
//7.0如果用會(huì)Uri.fromFile(XXX)會(huì)閃退,所以這里要特別做一個(gè)判斷。
//imgfile是圖片文件路徑
uri = Uri.fromFile(imgFile);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
}else{
//7.0+使用FileProvider.getUriForFile這個(gè)api
uri=FileProvider.getUriForFile(DemoActivity.this,
"com.lzw.demo.fileprovider",imgFile);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
//添加這一句表示對(duì)目標(biāo)應(yīng)用臨時(shí)授權(quán)該Uri所代表的文件
cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION );
}
startActivityForResult(cameraIntent, FLAG_CHOOSE_CAMERA);
}
想看到拍照、選擇照片、裁剪等完整流程的描述,可以參考這篇博客 解決安卓7.0拍照,相冊(cè)選擇崩潰的問題(包括壓縮圖片在內(nèi))
五、關(guān)于Android7.0相機(jī)閃退以及相冊(cè)獲取不到圖片問題
- 1、沒有動(dòng)態(tài)申請(qǐng)權(quán)限,按照上述思路去做就好了。
- 2、華為手機(jī)的一些特殊處理方式,詳情參見 ROM適配 http://www.itdecent.cn/p/f9c67a4b908e
六、Android 8.0適配報(bào)錯(cuò):Only fullscreen opaque activities can request orientation解決方案:
出現(xiàn)的原因:絕大多數(shù)都是因?yàn)槲覀優(yōu)榱颂岣哂脩趔w驗(yàn),手動(dòng)取消App啟動(dòng)白屏或者黑屏的時(shí)候,將Splash界面設(shè)為了透明,然后這個(gè)時(shí)候又設(shè)置了方向?yàn)榇怪?,從而?dǎo)致了這個(gè)問題。
解決方案:
-
1.找到你設(shè)置透明的Activity,然后在他的theme中將android:windowIsTranslucent改為false
<item name="android:windowIsTranslucent">false</item> -
2.再加入下面這行代碼就搞定了。
<item name="android:windowDisablePreview">true</item>
這個(gè)坑來自于博客: http://www.itdecent.cn/p/d0d907754603
七、Android8.0版本更新相關(guān)api適配
- 創(chuàng)建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel mChannel = new NotificationChannel("channel_01",
"消息推送", NotificationManager.IMPORTANCE_DEFAULT);
manager.createNotificationChannel(mChannel);
}
- 創(chuàng)建Notification
Context context = DJApplication.getInstance();
Notification.Builder builder = new Notification.Builder(context);
builder.setTicker("開始下載");
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(DJApplication.getInstance().getResources(),
R.mipmap.ic_launcher));
builder.setAutoCancel(true);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentTitle("下載中");
builder.setContentIntent(pIntent);
builder.setContentText(text);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId("channel_01");//設(shè)置有效的通知渠道 ID,這個(gè)ID要和之前創(chuàng)建時(shí)候的Channel_ID相同
}
manager.notify(1, builder.build());
- 安裝apk權(quán)限
在 Android 8.0 中,安裝未知應(yīng)用權(quán)限提高了安裝未知來源應(yīng)用時(shí)的安全性。此權(quán)限與其他運(yùn)行時(shí)權(quán)限一樣,會(huì)與應(yīng)用綁定,在安裝時(shí)進(jìn)行提示,確保用戶授予使用安裝來源的權(quán)限后,此權(quán)限才會(huì)提示用戶安裝應(yīng)用。在運(yùn)行 Android 8.0 或更高版本的設(shè)備上使用此權(quán)限時(shí),惡意下載程序?qū)o法騙取用戶安裝未獲得預(yù)先授權(quán)的應(yīng)用,所以我們需要加入安裝apk文件的權(quán)限。
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
