Android適配全面總結(二)----版本適配

版權聲明:本文為博主原創文章(部分引用他人博文,已加上引用說明),未經博主允許不得轉載。http://www.lxweimin.com/p/49fa8ebc0105

轉載請標明出處:
http://www.lxweimin.com/p/49fa8ebc0105
本文出自 AWeiLoveAndroid的博客


上一篇文章講了 屏幕適配 http://www.lxweimin.com/p/7aa34434ad4d
這一篇文章講一下 版本適配 http://www.lxweimin.com/p/49fa8ebc0105
下一篇文章講一下 ROM適配 http://www.lxweimin.com/p/f9c67a4b908e

在我們的開發中,會對不同安卓版本做適配,比如我之前做過的項目中最低兼容到4.4,最高兼容是最新的系統7.1,由于不同版本的系統中部分API版本也不同,我就要對這些API做特殊處理。新的平臺有一些API不能使用舊的API,舊的平臺也使用不了新的API。所以這就要考驗我們開發人員的能力了。我這里簡單給出幾點我開發中使用過的一些方式,僅供參考:

一、同一個api在不同版本都存在,只是api的一些接口方法有變更。

這種情況是最好處理的,只要對版本號做判斷,對應的系統版本用相應的api方法就好了。為了好維護,建議做一個簡單的封裝。

舉例說明如下:

比如Notification在不同版本的兼容,舉例如下:

首先打開谷歌官方文檔,看看文檔里面的一些說明:

Notification官方文檔

1.Notification這個類是added in API level 1,一直都有,只是具體某些方法有變更。繼續往下看。

2.這個類有個說明,意思是Notification.Builder是新增的一個內部類,用它創建通知更方便。接著往下看。

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公共的構造方法,其中有3個參數的這個在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被棄用,請使用正數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. use shouldShowLights().

  • priority This field was deprecated in API level 26. use getImportance() instead.

  • sound This field was deprecated in API level 26. use getSound() instead.

  • vibrate This field was deprecated in API level 26. use getVibrationPattern().


二、Android6.0的動態權限介紹

因為Android6.0(API23)開始需要動態申請權限,需要手動申請的權限有8組(短信、電話、聯系人、存儲、位置、麥克風、日歷、相機),共24個,如下所示:

所屬權限組 權限
短信 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
聯系人 READ_CONTACTS
聯系人 WRITE_CONTACTS
聯系人 GET_ACCOUNTS
存儲 READ_EXTERNAL_STORAGE
存儲 WRITE_EXTERNAL_STORAGE
位置 ACCESS_FINE_LOCATION
位置 ACCESS_COARSE_LOCATION
麥克風 RECORD_AUDIO
日歷 READ_CALENDAR
日歷 WRITE_CALENDAR
相機 CAMERA
傳感器 BODY_SENSORS

注意:如果應用程序請求在AndroidManifest中列出的危險權限,并且應用程序已經在同一權限組中具有另一個危險權限,系統會立即授予權限,而不會與用戶進行任何交互。
例如,如果一個應用程序先前已經請求并被授予READ_CONTACTS權限,然后它請求WRITE_CONTACTS(同屬于聯系人一組),系統會立即授予該權限,不會再彈出權限授予詢問的對話框。


三、Android6.0如何申請動態權限

開發中經常會遇到拍照的權限申請,這里就講一下如何動態設置拍照權限:

//別忘記在清單文件也加上CAMERA權限
//<uses-permission android:name="android.permission.CAMERA" />

// 定義識別碼
public static final int CAMERA_OK = 1;

//動態申請拍照權限
if (Build.VERSION.SDK_INT>22){
   if (ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA)
        != PackageManager.PERMISSION_GRANTED){
           //先判斷有沒有權限 ,沒有就在這里進行權限的申請
           requestPermissions(new String[]{Manifest.permission.CAMERA}, CAMERA_OK);       
    }else {
            //說明已經獲取到攝像頭權限了,可以去選擇照片或者拍照了。
            toSelectPhotoOrOpenCamera();
    }
}else {
      //這個說明系統版本在6.0之下,不需要動態獲取權限,直接去選擇照片或者拍照。
      toSelectPhotoOrOpenCamera();
}

//在Activity中重寫權限獲取方法:

/**
* 權限操作結果處理
*/
@Override
public void onRequestPermissionsResult(int requestCode,
                       String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case CAMERA_OK: 
            if (grantResults.length > 0 && grantResults[0]
                         == PackageManager.PERMISSION_GRANTED) {
                 //用戶已授權
                toSelectPhotoOrOpenCamera();
            } else {
                //用戶拒絕權限
                ToastUtils.show(this, 
                    "缺少相機權限,暫時無法提供掃描功能,請嘗試在設置中打開相機權限!", 
                    Toast.LENGTH_LONG);
            }
            break;
        }
    }
}

四、Android7.0對文件權限進一步升級,提出了新的類FileProvider來獲取文件。所以適配的時候一定要注意這一點api的變化。

FileProviderContentProvider的子類,把原來文件共享的 file://uri 換成了 content://uri 。一個Uri允許你獲取臨時權限去讀寫文件,當使用含有Uri的Intent,可以使用Intent.setFlags來添加臨時權限。

下面來看看調用系統相機拍攝照片有如何變化,大致步驟如下所示

(一)在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>

(二)配置你要獲取的文件所在的文件夾 --> 創建一個xml文件,比如file_demo.xml,文件內容如下:

<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>

(四)現在可以去拍照了。(由于Android6.0開始要動態申請權限,所以別忘了,這里就不寫了,主要講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);
        //添加這一句表示對目標應用臨時授權該Uri所代表的文件
        cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION );
    }
    startActivityForResult(cameraIntent, FLAG_CHOOSE_CAMERA);
}

想看到拍照、選擇照片、裁剪等完整流程的描述,可以參考這篇博客 解決安卓7.0拍照,相冊選擇崩潰的問題(包括壓縮圖片在內)


五、關于Android7.0相機閃退以及相冊獲取不到圖片問題


六、Android 8.0適配報錯:Only fullscreen opaque activities can request orientation解決方案:

出現的原因:絕大多數都是因為我們為了提高用戶體驗,手動取消App啟動白屏或者黑屏的時候,將Splash界面設為了透明,然后這個時候又設置了方向為垂直,從而導致了這個問題。

解決方案:

  • 1.找到你設置透明的Activity,然后在他的theme中將android:windowIsTranslucent改為false

      <item name="android:windowIsTranslucent">false</item>
    
  • 2.再加入下面這行代碼就搞定了。

      <item name="android:windowDisablePreview">true</item>
    

這個坑來自于博客: http://www.lxweimin.com/p/d0d907754603


七、Android8.0版本更新相關api適配

  • 創建通知渠道
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);
        }
  • 創建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要和之前創建時候的Channel_ID相同
        }
        manager.notify(1,  builder.build());
  • 安裝apk權限

在 Android 8.0 中,安裝未知應用權限提高了安裝未知來源應用時的安全性。此權限與其他運行時權限一樣,會與應用綁定,在安裝時進行提示,確保用戶授予使用安裝來源的權限后,此權限才會提示用戶安裝應用。在運行 Android 8.0 或更高版本的設備上使用此權限時,惡意下載程序將無法騙取用戶安裝未獲得預先授權的應用,所以我們需要加入安裝apk文件的權限。

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容