版權(quán)聲明:本文為博主原創(chuàng)文章(部分引用他人博文女阀,已加上引用說明)译打,未經(jīng)博主允許不得轉(zhuǎn)載智哀。http://www.reibang.com/p/49fa8ebc0105
轉(zhuǎn)載請標明出處:
http://www.reibang.com/p/49fa8ebc0105
本文出自 AWeiLoveAndroid的博客
上一篇文章講了 屏幕適配 http://www.reibang.com/p/7aa34434ad4d
這一篇文章講一下 版本適配 http://www.reibang.com/p/49fa8ebc0105
下一篇文章講一下 ROM適配 http://www.reibang.com/p/f9c67a4b908e
在我們的開發(fā)中,會對不同安卓版本做適配贸呢,比如我之前做過的項目中最低兼容到4.4镰烧,最高兼容是最新的系統(tǒng)7.1,由于不同版本的系統(tǒng)中部分API版本也不同楞陷,我就要對這些API做特殊處理怔鳖。新的平臺有一些API不能使用舊的API,舊的平臺也使用不了新的API固蛾。所以這就要考驗我們開發(fā)人員的能力了结执。我這里簡單給出幾點我開發(fā)中使用過的一些方式,僅供參考:
一艾凯、同一個api在不同版本都存在献幔,只是api的一些接口方法有變更。
這種情況是最好處理的览芳,只要對版本號做判斷斜姥,對應的系統(tǒng)版本用相應的api方法就好了。為了好維護沧竟,建議做一個簡單的封裝铸敏。
舉例說明如下:
比如Notification在不同版本的兼容,舉例如下:
首先打開谷歌官方文檔悟泵,看看文檔里面的一些說明:
1.Notification這個類是added in API level 1杈笔,一直都有,只是具體某些方法有變更糕非。繼續(xù)往下看蒙具。
2.這個類有個說明,意思是Notification.Builder是新增的一個內(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個參數(shù)的這個在api 11過時篱昔,它被Notification.Builder替代了。
Notification(int icon, CharSequence tickerText, long when)
This constructor was deprecated in API level 11.
Use Notification.Builder instead.
4.常量
EXTRA_LARGE_ICON
This constant was deprecated in API level 26. Use getLargeIcon(), which supports a wider variety of icon sources.(在API級別26中已棄用始腾。使用getLargeIcon()州刽,它支持更多種圖標源。)EXTRA_SMALL_ICON
This constant was deprecated in API level 26. Use getSmallIcon(), which supports a wider variety of icon sources.(在API級別26中已棄用浪箭。使用getSmallIcon()穗椅,它支持更多種圖標源。)FLAG_HIGH_PRIORITY
This constant was deprecated in API level 16. Use priority with a positive value.(在api16被棄用奶栖,請使用正數(shù)priority值替代)FLAG_SHOW_LIGHTS
This constant was deprecated in API level 26. use shouldShowLights().(在API級別26中已棄用匹表。請使用shouldShowLights()
替代)PRIORITY_DEFAULT
This constant was deprecated in API level 26. use IMPORTANCE_DEFAULT instead.(在API級別26中已棄用门坷。請使用IMPORTANCE_DEFAULT
替代)PRIORITY_HIGH
This constant was deprecated in API level 26. use IMPORTANCE_HIGH instead.(在API級別26中已棄用。請使用IMPORTANCE_HIGH
替代)PRIORITY_LOW
This constant was deprecated in API level 26. use IMPORTANCE_LOW instead.(在API級別26中已棄用桑孩。請使用IMPORTANCE_LOW
替代)PRIORITY_MAX
This constant was deprecated in API level 26. use IMPORTANCE_HIGH instead.(在API級別26中已棄用拜鹤。請使用IMPORTANCE_HIGH
替代)PRIORITY_MIN
This constant was deprecated in API level 26. use IMPORTANCE_MIN instead.(在API級別26中已棄用。請使用IMPORTANCE_MIN
替代)STREAM_DEFAULT
This constant was deprecated in API level 21. Use getAudioAttributes() instead.(在API級別21中已棄用流椒。請使用getAudioAttributes()
替代)
5.字段Fields
audioAttributes
在api 26棄用. 使用getAudioAttributes()
替代.audioStreamType
在api 21棄用. 使用audioAttributes
替代.defaults
此字段在API 26棄用敏簿。使用getSound()
和shouldShowLights()
和shouldVibrate()
。icon
此字段已在API級別26中棄用宣虾。使用setSmallIcon(Icon)
替代惯裕。largeIcon
This field was deprecated in API level 23. Use `setLargeIcon(Icon) instead.ledARGB
This field was deprecated in API level 26. use `shouldShowLights().ledOffMS
This field was deprecated in API level 26. use `shouldShowLights().ledOnMS
This field was deprecated in API level 26. useshouldShowLights().
priority
This field was deprecated in API level 26. usegetImportance()
instead.sound
This field was deprecated in API level 26. usegetSound()
instead.vibrate
This field was deprecated in API level 26. usegetVibrationPattern()
.
二、Android6.0的動態(tài)權(quán)限介紹
因為Android6.0(API23)開始需要動態(tài)申請權(quán)限绣硝,需要手動申請的權(quán)限有8組(短信蜻势、電話、聯(lián)系人鹉胖、存儲握玛、位置、麥克風甫菠、日歷挠铲、相機
),共24個寂诱,如下所示:
所屬權(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 |
存儲 | READ_EXTERNAL_STORAGE |
存儲 | WRITE_EXTERNAL_STORAGE |
位置 | ACCESS_FINE_LOCATION |
位置 | ACCESS_COARSE_LOCATION |
麥克風 | RECORD_AUDIO |
日歷 | READ_CALENDAR |
日歷 | WRITE_CALENDAR |
相機 | CAMERA |
傳感器 | BODY_SENSORS |
注意:如果應用程序請求在AndroidManifest中列出的危險權(quán)限拂苹,并且應用程序已經(jīng)在同一權(quán)限組中具有另一個危險權(quán)限,系統(tǒng)會立即授予權(quán)限痰洒,而不會與用戶進行任何交互瓢棒。
例如,如果一個應用程序先前已經(jīng)請求并被授予READ_CONTACTS權(quán)限丘喻,然后它請求WRITE_CONTACTS(同屬于聯(lián)系人一組)脯宿,系統(tǒng)會立即授予該權(quán)限,不會再彈出權(quán)限授予詢問的對話框泉粉。
三连霉、Android6.0如何申請動態(tài)權(quán)限
開發(fā)中經(jīng)常會遇到拍照的權(quán)限申請,這里就講一下如何動態(tài)設置拍照權(quán)限:
//別忘記在清單文件也加上CAMERA權(quán)限
//<uses-permission android:name="android.permission.CAMERA" />
// 定義識別碼
public static final int CAMERA_OK = 1;
//動態(tài)申請拍照權(quán)限
if (Build.VERSION.SDK_INT>22){
if (ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED){
//先判斷有沒有權(quán)限 搀继,沒有就在這里進行權(quán)限的申請
requestPermissions(new String[]{Manifest.permission.CAMERA}, CAMERA_OK);
}else {
//說明已經(jīng)獲取到攝像頭權(quán)限了,可以去選擇照片或者拍照了翠语。
toSelectPhotoOrOpenCamera();
}
}else {
//這個說明系統(tǒng)版本在6.0之下叽躯,不需要動態(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,
"缺少相機權(quán)限点骑,暫時無法提供掃描功能,請嘗試在設置中打開相機權(quán)限!",
Toast.LENGTH_LONG);
}
break;
}
}
}
四酣难、Android7.0對文件權(quán)限進一步升級,提出了新的類FileProvider來獲取文件黑滴。所以適配的時候一定要注意這一點api的變化憨募。
FileProvider
是ContentProvider
的子類,把原來文件共享的 file://uri
換成了 content://uri
袁辈。一個Uri允許你獲取臨時權(quán)限去讀寫文件菜谣,當使用含有Uri的Intent,可以使用Intent.setFlags來添加臨時權(quán)限晚缩。
下面來看看調(diào)用系統(tǒng)相機拍攝照片有如何變化尾膊,大致步驟如下所示:
(一)在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)建一個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/" />
<!--相當于 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開始要動態(tài)申請權(quán)限,所以別忘了鸣皂,這里就不寫了抓谴,主要講FileProvider的使用)
//適配7.0的fileprovider,imgfile是圖片文件路徑
public void TakePhotoAdaption(File imgFile){
Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//適配android7.0 手機拍照取uri的處理
if(Build.VERSION.SDK_INT<24){
//7.0如果用會Uri.fromFile(XXX)會閃退寞缝,所以這里要特別做一個判斷癌压。
//imgfile是圖片文件路徑
uri = Uri.fromFile(imgFile);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
}else{
//7.0+使用FileProvider.getUriForFile這個api
uri=FileProvider.getUriForFile(DemoActivity.this,
"com.lzw.demo.fileprovider",imgFile);
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
//添加這一句表示對目標應用臨時授權(quán)該Uri所代表的文件
cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION );
}
startActivityForResult(cameraIntent, FLAG_CHOOSE_CAMERA);
}
想看到拍照、選擇照片第租、裁剪等完整流程的描述措拇,可以參考這篇博客 解決安卓7.0拍照,相冊選擇崩潰的問題(包括壓縮圖片在內(nèi))
五慎宾、關(guān)于Android7.0相機閃退以及相冊獲取不到圖片問題
- 1丐吓、沒有動態(tài)申請權(quán)限,按照上述思路去做就好了趟据。
- 2券犁、華為手機的一些特殊處理方式,詳情參見 ROM適配 http://www.reibang.com/p/f9c67a4b908e
六汹碱、Android 8.0適配報錯:Only fullscreen opaque activities can request orientation解決方案:
出現(xiàn)的原因:絕大多數(shù)都是因為我們?yōu)榱颂岣哂脩趔w驗粘衬,手動取消App啟動白屏或者黑屏的時候,將Splash界面設為了透明咳促,然后這個時候又設置了方向為垂直稚新,從而導致了這個問題。
解決方案:
-
1.找到你設置透明的Activity跪腹,然后在他的theme中將android:windowIsTranslucent改為false
<item name="android:windowIsTranslucent">false</item>
-
2.再加入下面這行代碼就搞定了褂删。
<item name="android:windowDisablePreview">true</item>
這個坑來自于博客: http://www.reibang.com/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");//設置有效的通知渠道 ID冲茸,這個ID要和之前創(chuàng)建時候的Channel_ID相同
}
manager.notify(1, builder.build());
- 安裝apk權(quán)限
在 Android 8.0 中屯阀,安裝未知應用權(quán)限提高了安裝未知來源應用時的安全性缅帘。此權(quán)限與其他運行時權(quán)限一樣,會與應用綁定难衰,在安裝時進行提示钦无,確保用戶授予使用安裝來源的權(quán)限后,此權(quán)限才會提示用戶安裝應用盖袭。在運行 Android 8.0 或更高版本的設備上使用此權(quán)限時失暂,惡意下載程序?qū)o法騙取用戶安裝未獲得預先授權(quán)的應用,所以我們需要加入安裝apk文件的權(quán)限苍凛。
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />