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

版權(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在不同版本的兼容,舉例如下:

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

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_ICON This 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_ICON This 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_PRIORITY This constant was deprecated in API level 16. Use priority with a positive value.(在api16被棄用,請(qǐng)使用正數(shù)priority值替代)

  • FLAG_SHOW_LIGHTS This constant was deprecated in API level 26. use shouldShowLights().(在API級(jí)別26中已棄用。請(qǐng)使用 shouldShowLights() 替代)

  • PRIORITY_DEFAULT This constant was deprecated in API level 26. use IMPORTANCE_DEFAULT instead.(在API級(jí)別26中已棄用。請(qǐng)使用 IMPORTANCE_DEFAULT 替代)

  • PRIORITY_HIGH This constant was deprecated in API level 26. use IMPORTANCE_HIGH instead.(在API級(jí)別26中已棄用。請(qǐng)使用 IMPORTANCE_HIGH 替代)

  • PRIORITY_LOW This constant was deprecated in API level 26. use IMPORTANCE_LOW instead.(在API級(jí)別26中已棄用。請(qǐng)使用 IMPORTANCE_LOW 替代)

  • PRIORITY_MAX This constant was deprecated in API level 26. use IMPORTANCE_HIGH instead.(在API級(jí)別26中已棄用。請(qǐng)使用 IMPORTANCE_HIGH 替代)

  • PRIORITY_MIN This constant was deprecated in API level 26. use IMPORTANCE_MIN instead.(在API級(jí)別26中已棄用。請(qǐng)使用 IMPORTANCE_MIN 替代)

  • STREAM_DEFAULT This 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)替代。

  • 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的動(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的變化。

FileProviderContentProvider的子類,把原來文件共享的 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" />

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容