<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Android各版本迭代改動(dòng)與適配集合!

          共 14684字,需瀏覽 30分鐘

           ·

          2021-03-17 21:28

          來源|積木zz 

          https://juejin.cn/post/6860370635664261128

          前言

          今天分享的面試題是:

          Android在版本迭代中,總會(huì)進(jìn)行很多改動(dòng),那么你熟知各版本都改動(dòng)了什么內(nèi)容?又要怎么適配呢?

          Android4.4

          • 發(fā)布ART虛擬機(jī),提供選項(xiàng)可以開啟。
          • HttpURLConnection的底層實(shí)現(xiàn)改為了OkHttp。

            Android5.0

          • ART成為默認(rèn)虛擬機(jī),完全代替Dalvik虛擬機(jī)。
          • Context.bindService() 方法需要顯式 Intent,如果提供隱式 intent,將引發(fā)異常。

          Android6.0

          • 增加運(yùn)行時(shí)權(quán)限限制

          如果你的應(yīng)用使用到了危險(xiǎn)權(quán)限,比如在運(yùn)行時(shí)進(jìn)行檢查和請(qǐng)求權(quán)限。checkSelfPermission()方法用于檢查權(quán)限,requestPermissions() 方法用于請(qǐng)求權(quán)限。

          • 取消支持Apache HTTP

          Android 6.0 版移除了對(duì) Apache HTTP相關(guān)類庫的支持。要繼續(xù)使用 Apache HTTP API,您必須先在 build.gradle 文件中聲明以下編譯時(shí)依賴項(xiàng):

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

          有的小伙伴可能不熟悉這是啥,簡單說下:

          Apache HttpClient 是Apache開源組織提供的一個(gè)開源的項(xiàng)目,它是一個(gè)簡單的HTTP客戶端(并不是瀏覽器),可以發(fā)送HTTP請(qǐng)求,接受HTTP響應(yīng)。

          所以說白了,其實(shí)就是一個(gè)請(qǐng)求網(wǎng)絡(luò)的項(xiàng)目框架。

          Android 7.0

          • Android 7.0 引入一項(xiàng)新的應(yīng)用簽名方案 APK Signature Scheme v2

          • Toast導(dǎo)致的BadTokenException

          • 在Android7.0系統(tǒng)上,Android 框架強(qiáng)制執(zhí)行了 StrictMode API 政策禁止向你的應(yīng)用外公開 file:// URI。如果一項(xiàng)包含文件 file:// URI類型 的 Intent 離開你的應(yīng)用,應(yīng)用失敗,并出現(xiàn) FileUriExposedException 異常,如調(diào)用系統(tǒng)相機(jī)拍照錄制視頻,或裁切照片。

          這一點(diǎn)其實(shí)就是限制了在應(yīng)用間共享文件,如果需要在應(yīng)用間共享,需要授予要訪問的URI臨時(shí)訪問權(quán)限,我們要做的就是注冊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">
           //代表設(shè)備的根目錄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

          • 修改運(yùn)行時(shí)權(quán)限錯(cuò)誤

          Android 8.0 之前,如果應(yīng)用在運(yùn)行時(shí)請(qǐng)求權(quán)限并且被授予該權(quán)限,系統(tǒng)會(huì)錯(cuò)誤地將屬于同一權(quán)限組并且在清單中注冊的其他權(quán)限也一起授予應(yīng)用。對(duì)于針對(duì) Android 8.0 的應(yīng)用,系統(tǒng)只會(huì)授予應(yīng)用明確請(qǐng)求的權(quán)限。然而,一旦用戶為應(yīng)用授予某個(gè)權(quán)限,則所有后續(xù)對(duì)該權(quán)限組中權(quán)限的請(qǐng)求都將被自動(dòng)批準(zhǔn)。

          也就是說,以前你申請(qǐng)了READ_EXTERNAL_STORAGE權(quán)限,應(yīng)用會(huì)同時(shí)給你授予同權(quán)限組的WRITE_EXTERNAL_STORAGE權(quán)限。如果Android8.0以上,只會(huì)給你授予你請(qǐng)求的READ_EXTERNAL_STORAGE權(quán)限。如果需要WRITE_EXTERNAL_STORAGE權(quán)限,還要單獨(dú)申請(qǐng),不過系統(tǒng)會(huì)立即授予,不會(huì)提示。

          • 修改通知

          Android 8.0 對(duì)于通知修改了很多,比如通知渠道、通知標(biāo)志、通知超時(shí)、背景顏色。其中比較重要的就是通知渠道,其允許您為要顯示的每種通知類型創(chuàng)建用戶可自定義的渠道。

          這樣的好處就是對(duì)于某個(gè)應(yīng)用可以把權(quán)限分成很多類,用戶來控制是否顯示哪些類別的通知。而開發(fā)者要做的就是必須設(shè)置這個(gè)渠道id,否則通知可能會(huì)失效。

          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, "廣告");

                      //創(chuàng)建group
                      notificationManager.createNotificationChannelGroup(group);

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

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

             //創(chuàng)建通知時(shí),標(biāo)記你的渠道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
          }
          • 不允許安裝未知來源的應(yīng)用

          Android 8.0去除了“允許未知來源”選項(xiàng),所以如果我們的App有安裝App的功能(檢查更新之類的),那么會(huì)無法正常安裝。

          <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) {
                          //安裝應(yīng)用
                      } else {
                          //跳轉(zhuǎn)至“安裝未知應(yīng)用”權(quán)限界面,引導(dǎo)用戶開啟權(quán)限
                          Uri selfPackageUri = Uri.parse("package:" + this.getPackageName());
                          Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES, selfPackageUri);
                          startActivityForResult(intent, 100);
                      }
                  }else {
                      //安裝應(yīng)用
                  }

              }

              //接收“安裝未知應(yīng)用”權(quán)限的開啟結(jié)果
              @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才可以設(shè)置方向。這應(yīng)該是個(gè)bug,在Android8.0中出現(xiàn),8.1中被修復(fù)。

          我們的處理辦法就是要么去掉設(shè)置方向的代碼,要么舍棄透明效果。

          Android9.0

          • 在9.0中默認(rèn)情況下啟用網(wǎng)絡(luò)傳輸層安全協(xié)議 (TLS),默認(rèn)情況下已停用明文支持。也就是不允許使用http請(qǐng)求,要求使用https。解決辦法就是添加網(wǎng)絡(luò)安全配置:
          <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中取消了對(duì)Apache HTTP 客戶端的支持,Android9.0中直接移除了該庫,要使用的話需要添加配置:

          <uses-library android:name="org.apache.http.legacy" android:required="false"/>
          • 前臺(tái)服務(wù)調(diào)用

          Android 9.0 要求創(chuàng)建一個(gè)前臺(tái)服務(wù)需要請(qǐng)求 FOREGROUND_SERVICE 權(quán)限,否則系統(tǒng)會(huì)引發(fā) 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環(huán)境中啟動(dòng)Activity

          在9.0 中,不能直接非 Activity 環(huán)境中(比如Service,Application)啟動(dòng) Activity,否則會(huì)崩潰報(bào)錯(cuò),解決辦法就是加上FLAG_ACTIVITY_NEW_TASK

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

          Android10

          • 分區(qū)存儲(chǔ)

          Android10中默認(rèn)開啟了分區(qū)存儲(chǔ),也就是沙盒模式。應(yīng)用只能看到本應(yīng)用專有的目錄(通過 Context.getExternalFilesDir() 訪問)以及特定類型的媒體。

          如果需要關(guān)閉這個(gè)功能可以配置:

          android:requestLegacyExternalStorage="true"

          分區(qū)存儲(chǔ)下,訪問文件的方法:

          1)應(yīng)用專屬目錄

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

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

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

          val cursor = contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, nullnullnull"${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(存儲(chǔ)訪問框架--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: IntdataIntent?) {
                  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")
                  }
              }

          • 權(quán)限再次升級(jí)

          從Android10開始普通應(yīng)用不再允許請(qǐng)求權(quán)限android.permission.READ_PHONE_STATE。而且,無論你的App是否適配過Android Q(既targetSdkVersion是否大于等于29),均無法再獲取到設(shè)備IMEI等設(shè)備信息。

          如果Android10以下設(shè)備獲取設(shè)備IMEI等信息,可以配置最大sdk版本:

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

          Android 11

          • 分區(qū)存儲(chǔ)強(qiáng)制執(zhí)行

          沒錯(cuò),Android11強(qiáng)制執(zhí)行分區(qū)存儲(chǔ),也就是沙盒模式。這次真的沒有關(guān)閉功能了,離Android11出來也有一段時(shí)間了,還是抓緊適配把。

          • 修改電話權(quán)限

          改動(dòng)了兩個(gè)API:getLine1Number()和 getMsisdn() ,需要加上READ_PHONE_NUMBERS權(quán)限

          • 不允許自定義toast從后臺(tái)顯示了

          • 必須加上v2簽名

          • 增加5g相關(guān)API

          • 后臺(tái)位置訪問權(quán)限再次限制

          你一定很奇怪,為什么Android11的適配就這么草草收尾了?這可是我們最需要的?。?/p>

          哈哈,因?yàn)楦膭?dòng)還是挺多的,所以給你推薦文章—Android11最全適配指南,應(yīng)該有很多朋友都看過了:https://juejin.cn/post/6860370635664261128,或者點(diǎn)擊文末的“閱讀原文”即可。

          參考

          https://juejin.cn/post/6898176468661059597 https://blog.csdn.net/qq_17766199/article/details/80965631 https://weilu.blog.csdn.net/article/details/98336225


          ·················END·················

          推薦閱讀

          ? 耗時(shí)2年,Android進(jìn)階三部曲第三部《Android進(jìn)階指北》出版!

          ? 『BATcoder』做了多年安卓還沒編譯過源碼?一個(gè)視頻帶你玩轉(zhuǎn)!

          ? 『BATcoder』是時(shí)候下載Android11系統(tǒng)源碼和內(nèi)核源碼了!

          推薦我的技術(shù)博客

          推薦一下我的獨(dú)立博客: liuwangshu.cn ,內(nèi)含Android最強(qiáng)原創(chuàng)知識(shí)體系,一直在更新,歡迎體驗(yàn)和收藏!

          BATcoder技術(shù)群,讓一部分人先進(jìn)大廠

          你好,我是劉望舒,百度百科收錄的騰訊云TVP專家,著有暢銷書《Android進(jìn)階之光》《Android進(jìn)階解密》《Android進(jìn)階指北》,蟬聯(lián)四屆電子工業(yè)出版社年度優(yōu)秀作者,谷歌開發(fā)者社區(qū)特邀講師。

          前華為面試官,現(xiàn)大廠技術(shù)負(fù)責(zé)人。


          歡迎添加我的微信 henglimogan ,備注:BATcoder,加入BATcoder技術(shù)群。



          明天見(??ω??)
          瀏覽 42
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  97超碰资源总站 | 啪啪啪啪网站 | 操逼网站视频 | 狼友精品在线观看 | 色老板在线观看视频 |