Android各版本迭代改動與適配集合

前言

今天分享的面試題是:java

Android在版本迭代中,總會進行不少改動,那麼你熟知各版本都改動了什麼內容?又要怎麼適配呢?android

Android4.4

  • 發佈ART虛擬機,提供選項能夠開啓。
  • HttpURLConnection的底層實現改成了OkHttp。

Android5.0

  • ART成爲默認虛擬機,徹底代替Dalvik虛擬機。
  • Context.bindService() 方法須要顯式 Intent,若是提供隱式 intent,將引起異常。

Android6.0

  • 增長運行時權限限制

若是你的應用使用到了危險權限,好比在運行時進行檢查和請求權限。checkSelfPermission()方法用於檢查權限,requestPermissions() 方法用於請求權限。面試

  • 取消支持Apache HTTP

Android 6.0 版移除了對 Apache HTTP相關類庫的支持。要繼續使用 Apache HTTP API,您必須先在 build.gradle 文件中聲明如下編譯時依賴項:apache

android {useLibrary 'org.apache.http.legacy'}

有的小夥伴可能不熟悉這是啥,簡單說下:瀏覽器

Apache HttpClient 是Apache開源組織提供的一個開源的項目,它是一個簡單的HTTP客戶端(並非瀏覽器),能夠發送HTTP請求,接受HTTP響應。安全

因此說白了,其實就是一個請求網絡的項目框架。網絡

Android 7.0

  • Android 7.0 引入一項新的應用簽名方案 APK Signature Scheme v2app

  • Toast致使的BadTokenException框架

  • 在Android7.0系統上,Android 框架強制執行了 StrictMode API 政策禁止向你的應用外公開 file:// URI。 若是一項包含文件 file:// URI類型 的 Intent 離開你的應用,應用失敗,並出現 FileUriExposedException 異常,如調用系統相機拍照錄制視頻,或裁切照片。ide

這一點其實就是限制了在應用間共享文件,若是須要在應用間共享,須要授予要訪問的URI臨時訪問權限,咱們要作的就是註冊FileProvider

1)聲明FileProvider。

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="app的包名.fileProvider"
    android:grantUriPermissions="true"
    android:exported="false">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>
<!--androidx版本類路徑爲:androidx.core.content.FileProvider-->

2)編寫xml文件,肯定可訪問的目錄

<paths xmlns:android="http://schemas.android.com/apk/res/android">
	//表明設備的根目錄new File("/");
    <root-path name="root" path="." /> 
    //context.getFilesDir()
    <files-path name="files" path="." /> 
    //context.getCacheDir()
    <cache-path name="cache" path="." /> 
    //Environment.getExternalStorageDirectory()
    <external-path name="external" path="." />
    //context.getExternalFilesDirs()
    <external-files-path name="name" path="path" />
    //getExternalCacheDirs()
     <external-cache-path name="name" path="path" />
</paths>

3)使用FileProvider

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    Uri uri = FileProvider.getUriForFile(CameraActivity.this, "app的包名.fileProvider", photoFile);
} else {
    Uri uri = Uri.fromFile(photoFile);
}

Android8.0

  • 修改運行時權限錯誤

Android 8.0 以前,若是應用在運行時請求權限而且被授予該權限,系統會錯誤地將屬於同一權限組而且在清單中註冊的其餘權限也一塊兒授予應用。
對於針對 Android 8.0 的應用,系統只會授予應用明確請求的權限。然而,一旦用戶爲應用授予某個權限,則全部後續對該權限組中權限的請求都將被自動批准。

也就是說,之前你申請了READ_EXTERNAL_STORAGE權限,應用會同時給你授予同權限組的WRITE_EXTERNAL_STORAGE權限。若是Android8.0以上,只會給你授予你請求的READ_EXTERNAL_STORAGE權限。若是須要WRITE_EXTERNAL_STORAGE權限,還要單獨申請,不過系統會當即授予,不會提示。

  • 修改通知

Android 8.0 對於通知修改了不少,好比通知渠道、通知標誌、通知超時、背景顏色。其中比較重要的就是通知渠道,其容許您爲要顯示的每種通知類型建立用戶可自定義的渠道。

這樣的好處就是對於某個應用能夠把權限分紅不少類,用戶來控制是否顯示哪些類別的通知。而開發者要作的就是必須設置這個渠道id,不然通知可能會失效。

private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

            NotificationManager notificationManager = (NotificationManager)
                    getSystemService(Context.NOTIFICATION_SERVICE);

            //分組(可選)
            //groupId要惟一
            String groupId = "group_001";
            NotificationChannelGroup group = new NotificationChannelGroup(groupId, "廣告");

            //建立group
            notificationManager.createNotificationChannelGroup(group);

            //channelId要惟一
            String channelId = "channel_001";

            NotificationChannel adChannel = new NotificationChannel(channelId,
                    "推廣信息", NotificationManager.IMPORTANCE_DEFAULT);
            //補充channel的含義(可選)
            adChannel.setDescription("推廣信息");
            //將渠道添加進組(先建立組才能添加)
            adChannel.setGroup(groupId);
            //建立channel
            notificationManager.createNotificationChannel(adChannel);

			//建立通知時,標記你的渠道id
            Notification notification = new Notification.Builder(MainActivity.this, channelId)
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                    .setContentTitle("一條新通知")
                    .setContentText("這是一條測試消息")
                    .setAutoCancel(true)
                    .build();
            notificationManager.notify(1, notification);

        }
    }
  • 懸浮窗

Android8.0以上必須使用新的窗口類型(TYPE_APPLICATION_OVERLAY)才能顯示提醒懸浮窗:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    mWindowParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
}else {
    mWindowParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
}
  • 不容許安裝未知來源的應用

Android 8.0去除了「容許未知來源」選項,因此若是咱們的App有安裝App的功能(檢查更新之類的),那麼會沒法正常安裝。

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

private void installAPK(){

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            boolean hasInstallPermission = getPackageManager().canRequestPackageInstalls();
            if (hasInstallPermission) {
                //安裝應用
            } else {
                //跳轉至「安裝未知應用」權限界面,引導用戶開啓權限
                Uri selfPackageUri = Uri.parse("package:" + this.getPackageName());
                Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, selfPackageUri);
                startActivityForResult(intent, 100);
            }
        }else {
            //安裝應用
        }

    }

    //接收「安裝未知應用」權限的開啓結果
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == 100) {
            installAPK();
        }
    }
  • Only fullscreen opaque activities can request orientation

只有全屏不透明的activity才能夠設置方向。這應該是個bug,在Android8.0中出現,8.1中被修復。

咱們的處理辦法就是要麼去掉設置方向的代碼,要麼捨棄透明效果。

Android9.0

  • 在9.0中默認狀況下啓用網絡傳輸層安全協議 (TLS),默認狀況下已停用明文支持。也就是不容許使用http請求,要求使用https。解決辦法就是添加網絡安全配置:
<application android:networkSecurityConfig="@xml/network_security_config">

<network-security-config>
 <base-config cleartextTrafficPermitted="true" />
</network-security-config>


<!--或者在AndroidManifest.xml中配置:
android:usesCleartextTraffic="true"
-->
  • 移除Apache HTTP 客戶端

在6.0中取消了對Apache HTTP 客戶端的支持,Android9.0中直接移除了該庫,要使用的話須要添加配置:

<uses-library android:name="org.apache.http.legacy" android:required="false"/>
  • 前臺服務調用

Android 9.0 要求建立一個前臺服務須要請求 FOREGROUND_SERVICE 權限,不然系統會引起 SecurityException。

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

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    startForegroundService(intentService);
} else {
    startService(intentService);
}
  • 不能在非Acitivity環境中啓動Activity

在9.0 中,不能直接非 Activity 環境中(好比Service,Application)啓動 Activity,不然會崩潰報錯,解決辦法就是加上FLAG_ACTIVITY_NEW_TASK

Intent intent = new Intent(this, TestActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

Android10

  • 分區存儲

Android10中默認開啓了分區存儲,也就是沙盒模式。應用只能看到本應用專有的目錄(經過 Context.getExternalFilesDir() 訪問)以及特定類型的媒體。

若是須要關閉這個功能能夠配置:

android:requestLegacyExternalStorage="true"

分區存儲下,訪問文件的方法:

1)應用專屬目錄

//分區存儲空間
val file = File(context.filesDir, filename)

//應用專屬外部存儲空間
val appSpecificExternalDir = File(context.getExternalFilesDir(), filename)

2)訪問公共媒體目錄文件

val cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, "${MediaStore.MediaColumns.DATE_ADDED} desc")
if (cursor != null) {
    while (cursor.moveToNext()) {
        val id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))
        val uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
        println("image uri is $uri")
    }
    cursor.close()
}

3)SAF(存儲訪問框架--Storage Access Framework)

val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
    intent.addCategory(Intent.CATEGORY_OPENABLE)
    intent.type = "image/*"
    startActivityForResult(intent, 100)

    @RequiresApi(Build.VERSION_CODES.KITKAT)
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (data == null || resultCode != Activity.RESULT_OK) return
        if (requestCode == 100) {
            val uri = data.data
            println("image uri is $uri")
        }
    }
  • 權限再次升級

從Android10開始普通應用再也不容許請求權限android.permission.READ_PHONE_STATE。並且,不管你的App是否適配過Android Q(既targetSdkVersion是否大於等於29),均沒法再獲取到設備IMEI等設備信息。

若是Android10如下設備獲取設備IMEI等信息,能夠配置最大sdk版本:

<uses-permission android:name="android.permission.READ_PHONE_STATE"
        android:maxSdkVersion="28"/>

Android 11

  • 分區存儲強制執行

沒錯,Android11強制執行分區存儲,也就是沙盒模式。此次真的沒有關閉功能了,離Android11出來也有一段時間了,仍是抓緊適配把。

  • 修改電話權限

改動了兩個API:getLine1Number()和 getMsisdn() ,須要加上READ_PHONE_NUMBERS權限

  • 不容許自定義toast從後臺顯示了

  • 必須加上v2簽名

  • 增長5g相關API

  • 後臺位置訪問權限再次限制

你必定很奇怪,爲何Android11的適配就這麼草草收尾了?這但是咱們最須要的啊?

哈哈,由於改動仍是挺多的,因此給你推薦文章—Android11最全適配指南,應該有不少朋友都看過了:
https://juejin.cn/post/6860370635664261128

參考

https://juejin.cn/post/6898176468661059597
http://www.javashuo.com/article/p-xnjrtrcf-hv.html
https://weilu.blog.csdn.net/article/details/98336225

拜拜

有一塊兒學習的小夥伴能夠關注下❤️ 個人公衆號——碼上積木,天天剖析一個知識點,咱們一塊兒積累知識。公衆號回覆111可得到面試題《思考與解答》以往期刊。

相關文章
相關標籤/搜索