【Android】各版本改動一覽

文章推薦指數: 80 %
投票人數:10人

相反如果沒有intent-filter,那就不應該把Activity 的exported 設置為true ,這可能會在安全掃描時被定義為安全漏洞。

而在Android 12 的平台上,也 ... Google大哥每年都會對Android推出新的版本,一來感到興奮,不知道大哥又要端出什麼新玩意,但另一方面卻也感到害怕,不知道這次大哥又改動了什麼部分,要讓開發變得寸步難行(誤)。

截至今日Android已經推進到第13個版本了,然版本破碎化的問題依舊存在,每開發一個新的App就要先問,需要支援的版本幅度,然後再看各個功能有沒有被拿掉,各種煩悶,所以這邊就來簡單整理一下各個版本間的變動及對開發者的影響。

Adnroid12android:exported它主要是設置Activity是否可由其他應用的組件啟動,“true”則表示可以,而“false”表示不可以。

若為“false”,則Activity只能由同一應用的組件或使用同一用戶ID的不同應用啟動。

當然不止是Activity,Service和Receiver也會有exported的場景。

一般情況下如果使用了intent-filter,則不能將exported設置為“false”,不然在Activity被調用時系統會拋出ActivityNotFoundException異常。

相反如果沒有intent-filter,那就不應該把Activity的exported設置為true,這可能會在安全掃描時被定義為安全漏洞。

而在Android12的平台上,也就是使用targetSdkVersion31時,那麼你就需要注意:如果Activity、Service或Receiver使用intent-filter,並且未顯式聲明android:exported的值,App將會無法安裝。

‌‌資料來源:Android12快速适配Android11分割槽儲存強制執行‌‌Android11強制執行分割槽儲存,也就是沙盒模式。

這次無法像Android10一樣用配置的方式關閉這功能。

媒體文件訪問權‌‌為了在保證用戶隱私的同時可以更輕鬆地訪問媒體,Android11增加了以下功能。

執行批量操作和使用直接文件路徑和原生庫訪問文件。

執行批量操作這裡的批量操作指的是Android11向MediaStoreAPI中添加了多種方法,用於簡化特定媒體文件更改流程(例如在原位置編輯照片),分別是: createWriteRequest()用戶向應用授予對指定媒體文件組的寫入訪問權限的請求。

createFavoriteRequest()用戶將設備上指定的媒體文件標記為“收藏”的請求。

對該文件具有讀取訪問權限的任何應用都可以看到用戶已將該文件標記為“收藏”。

 createTrashRequest()用戶將指定的媒體文件放入設備垃圾箱的請求。

垃圾箱中的內容會在系統定義的時間段後被永久刪除。

createDeleteRequest()用戶立即永久刪除指定的媒體文件(而不是先將其放入垃圾箱)的請求。

直接文件路徑和原生庫訪問文件沒錯!Android11又恢復了使用直接文件路径訪問訪問媒體文件!哈哈,這樣就方便多了。

也就是除了MediaStoreAPI之外還有兩種方式可以訪問媒體文件:FileAPI。

原生庫,例如fopen()。

那Android10怎辦呢??要不就用MediaStore,要不就直接把分區存儲關了吧(requestLegacyExternalStorage=true)所有文件訪問權限雖然說了這麼多,但是還有些應用就要訪問所有文件,比如殺毒軟件,文件管理器。

放心,有辦法!MANAGE_EXTERNAL_STORAGE這不來了嗎。

這個權限就是用來獲取所有文件的管理權限: valintent=Intent() intent.action=Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION startActivity(intent) //判断是否获取MANAGE_EXTERNAL_STORAGE权限: valisHasStoragePermission=Environment.isExternalStorageManager()電話號碼相關權限Android11更改了您的應用在讀取電話號碼時使用的與電話相關的權限。

TelecomManager類中的getLine1Number()方法TelecomManager類中的getMsisdn()方法。

也就是當用到這兩個API的時候,原來的READ_PHONE_STATE權限不管用了,需要READ_PHONE_NUMBERS權限才行。

ActivityCompat.requestPermissions(this,arrayOf(Manifest.permission.READ_PHONE_STATE,Manifest.permission.READ_PHONE_NUMBERS),100)自定義Toast被屏蔽現在需要APK簽名方案v2對於以Android11(API級別30)為目標平台,且目前僅使用APK簽名方案v1簽名的應用,現在還必須使用APK簽名方案v2或更高版本進行簽名。

用戶無法在搭載Android11的設備上安裝或更新僅通過APK簽名方案v1簽名的應用。

媒體intent操作需要系統默認相機從Android11開始,只有預裝的系統相機應用可以響應以下intent操作:android.media.action.VIDEO_CAPTUREandroid.media.action.IMAGE_CAPTUREandroid.media.action.IMAGE_CAPTURE_SECURE也就是說,如果我調用intent喚起照相機,使用VIDEO_CAPTURE的action,只有系統的相機能夠響應,而第三方的相機應用不會響應了。

這點對普通的相機應用還是有點打擊的,官方給的建議是如果要使用特定的第三方相機應用來代表其捕獲圖片或視頻,可以通過為intent設置軟件包名稱或組件來使這些intent變得明確。

valintent=Intent() intent.action=android.provider.MediaStore.ACTION_IMAGE_CAPTURE startActivity(intent) //無法呼叫第三方相機,只能呼叫系統相機5GAndroid11添加了在您的應用中支持5G的功能。

包括:檢測是否連接到了5G網絡檢查按流量計費性。

後台位置信息訪問權限在搭載Android11的設備上,當應用中的某項功能請求在後台訪問位置信息時,用戶看到的系統對話框不再包含用於啟用後台位置信息訪問權限的按鈕。

如需啟用後台位置信息訪問權限,用戶必須在設置頁面上針對應用的位置權限設置一律允許選項。

valpermissionAccessCoarseLocationApproved=ActivityCompat .checkSelfPermission(this,permission.ACCESS_COARSE_LOCATION)== PackageManager.PERMISSION_GRANTED if(permissionAccessCoarseLocationApproved){ valbackgroundLocationPermissionApproved=ActivityCompat .checkSelfPermission(this,permission.ACCESS_BACKGROUND_LOCATION)== PackageManager.PERMISSION_GRANTED if(backgroundLocationPermissionApproved){ //前后台位置权限都有 }else{ //申请后台权限 if(applicationInfo.targetSdkVersion ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), 200) }).create().show() } } }else{ if(applicationInfo.targetSdkVersion元素,告知系統你要獲取哪些應用信息或者哪一類應用。

資料來源:拖不得了,Android11最全适配指南奉上Android10分割槽儲存‌‌Android10中預設開啟了分割槽儲存,也就是沙盒模式。

應用只能看到本應用專有的目錄(通過Context.getExternalFilesDir()訪問)以及特定型別的媒體‌‌如果需要關閉這個功能可以配置:android:requestLegacyExternalStorage="true"‌分割槽儲存下,訪問檔案的方法://分区存储空间 valfile=File(context.filesDir,filename) //应用专属外部存储空间 valappSpecificExternalDir=File(context.getExternalFilesDir(),filename)應用專屬目錄valcursor=contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,null,null,null,"${MediaStore.MediaColumns.DATE_ADDED}desc") if(cursor!=null){ while(cursor.moveToNext()){ valid=cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns._ID)) valuri=ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,id) println("imageuriis$uri") } cursor.close() }訪問公共媒體目錄檔案valintent=Intent(Intent.ACTION_OPEN_DOCUMENT) intent.addCategory(Intent.CATEGORY_OPENABLE) intent.type="image/*" startActivityForResult(intent,100) @RequiresApi(Build.VERSION_CODES.KITKAT) overridefunonActivityResult(requestCode:Int,resultCode:Int,data:Intent?){ super.onActivityResult(requestCode,resultCode,data) if(data==null||resultCode!=Activity.RESULT_OK)return if(requestCode==100){ valuri=data.data println("imageuriis$uri") } }SAF(儲存訪問框架--StorageAccessFramework)許可權再次升級‌‌從Android10開始普通應用不再允許請求許可權android.permission.READ_PHONE_STATE。

而且,無論你的App是否適配過AndroidQ(既targetSdkVersion是否大於等於29),均無法再獲取到裝置IMEI等裝置資訊。

如果Android10以下裝置獲取裝置IMEI等資訊,可以配置最大sdk版本:Android9.0在9.0中預設情況下啟用網路傳輸層安全協議(TLS),預設情況下已停用明文支援。

也就是不允許使用http請求,要求使用https。

解決辦法就是新增網路安全配置: 移除ApacheHTTP客戶端‌‌在6.0中取消了對ApacheHTTP客戶端的支援,Android9.0中直接移除了該庫,要使用的話需要新增配置:前臺服務呼叫‌‌Android9.0要求建立一個前臺服務需要請求FOREGROUND_SERVICE許可權,否則系統會引發SecurityException。

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_TASKIntentintent=newIntent(this,TestActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent);Android8.0修改執行時許可權錯誤‌‌在Android8.0之前,如果應用在執行時請求許可權並且被授予該許可權,系統會錯誤地將屬於同一許可權組並且在清單中註冊的其他許可權也一起授予應用。

對於針對Android8.0的應用,系統只會授予應用明確請求的許可權。

然而,一旦使用者為應用授予某個許可權,則所有後續對該許可權組中許可權的請求都將被自動批准。

‌‌‌‌也就是說,以前你申請了READ_EXTERNAL_STORAGE許可權,應用會同時給你授予同許可權組的WRITE_EXTERNAL_STORAGE許可權。

如果Android8.0以上,只會給你授予你請求的READ_EXTERNAL_STORAGE許可權。

如果需要WRITE_EXTERNAL_STORAGE許可權,還要單獨申請,不過系統會立即授予,不會提示。

修改通知‌‌Android8.0對於通知修改了很多,比如通知渠道、通知標誌、通知超時、背景顏色。

其中比較重要的就是通知渠道,其允許您為要顯示的每種通知型別建立使用者可自定義的渠道。

‌‌‌‌這樣的好處就是對於某個應用可以把許可權分成很多類,使用者來控制是否顯示哪些類別的通知。

而開發者要做的就是必須設定這個渠道id,否則通知可能會失效。

privatevoidcreateNotificationChannel(){ if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){ NotificationManagernotificationManager=(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); //分组(可选) //groupId要唯一 StringgroupId="group_001"; NotificationChannelGroupgroup=newNotificationChannelGroup(groupId,"广告"); //创建group notificationManager.createNotificationChannelGroup(group); //channelId要唯一 StringchannelId="channel_001"; NotificationChanneladChannel=newNotificationChannel(channelId, "推广信息",NotificationManager.IMPORTANCE_DEFAULT); //补充channel的含义(可选) adChannel.setDescription("推广信息"); //将渠道添加进组(先创建组才能添加) adChannel.setGroup(groupId); //创建channel notificationManager.createNotificationChannel(adChannel); //创建通知时,标记你的渠道id Notificationnotification=newNotification.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 }不允許安裝未知來源的應用‌‌Android8.0去除了“允許未知來源”選項,所以如果我們的App有安裝App的功能(檢查更新之類的),那麼會無法正常安裝。

privatevoidinstallAPK(){ if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){ booleanhasInstallPermission=getPackageManager().canRequestPackageInstalls(); if(hasInstallPermission){ //安装应用 }else{ //跳转至“安装未知应用”权限界面,引导用户开启权限 UriselfPackageUri=Uri.parse("package:"+this.getPackageName()); Intentintent=newIntent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,selfPackageUri); startActivityForResult(intent,100); } }else{ //安装应用 } } //接收“安装未知应用”权限的开启结果 @Override protectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){ super.onActivityResult(requestCode,resultCode,data); if(requestCode==100){ installAPK(); } } Onlyfullscreenopaqueactivitiescanrequestorientation‌‌只有全屏不透明的activity才可以設定方向。

這應該是個bug,在Android8.0中出現,8.1中被修復。

我們的處理辦法就是要麼去掉設定方向的程式碼,要麼捨棄透明效果。

Android7.0Android7.0引入一項新的應用簽名方案APKSignatureSchemev2Toast導致的BadTokenException在Android7.0系統上,Android框架強制執行了StrictModeAPI政策禁止向你的應用外公開file://URI。

如果一項包含檔案file://URI型別的Intent離開你的應用,應用失敗,並出現FileUriExposedException異常,如呼叫系統相機拍照錄制影片,或裁切照片。

這一點其實就是限制了在應用間共享檔案,如果需要在應用間共享,需要授予要訪問的URI臨時訪問許可權,我們要做的就是註冊FileProvider: 宣告FileProvider //代表设备的根目录newFile("/"); //context.getFilesDir() //context.getCacheDir() //Environment.getExternalStorageDirectory() //context.getExternalFilesDirs() //getExternalCacheDirs() 編寫xml檔案,確定可訪問的目錄if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){ Uriuri=FileProvider.getUriForFile(CameraActivity.this,"app的包名.fileProvider",photoFile); }else{ Uriuri=Uri.fromFile(photoFile); } 使用FileProviderAndroid6.0增加執行時許可權限制‌‌如果你的應用使用到了危險許可權,比如在執行時進行檢查和請求許可權。

checkSelfPermission()方法用於檢查許可權,requestPermissions()方法用於請求許可權。

取消支援ApacheHTTP‌‌Android6.0版移除了對ApacheHTTP相關類庫的支援。

要繼續使用ApacheHTTPAPI,您必須先在build.gradle檔案中宣告以下編譯時依賴項:android{useLibrary'org.apache.http.legacy'}Android5.0ART成為預設虛擬機器,完全代替Dalvik虛擬機器。

Context.bindService()方法需要顯式Intent,如果提供隱式intent,將引發異常。

Android4.4釋出ART虛擬機器,提供選項可以開啟。

HttpURLConnection的底層實現改為了OkHttp。

資料來源:Android各版本迭代改动与适配 Signupformorelikethis. Enteryouremail Subscribe 有一個很神秘的需求,需要對專案內的圖檔進行加密,但又不能影響到專案的開發。

於是在Github上發現了一個看似可用的解決方案,下面就來說說這是怎麼做到的。

原理在AndroidStudio當中,要透過gradle打包apk的時候,我們可以透過addBuildListener來監聽整個打包的過程。

在addBuildListener這個Listener裡面,有幾個方法:settingsEvaluated()、projectsLoaded()、projectsEvaluated()、buildFinished()。

這邊我們專注在projectsEvaluated()、buildFinished()這兩個方法,其中projectsEvaluated()會是在打包apk前執行的,而buildFinished()則是會在打包完成後執行。

因此我們可以利用這兩個方法,來對圖檔進行加密,在要打包成apk時,我們將圖檔們加密並放入apk中,等打包結束後,我們在把原檔還原回來。

至於apk 最近有個需求,需要利用WebView來播放本地端的檔案,看似簡單的需求,但卻會被Chromium給擋掉,原因是因為Chromium不允許直接讀取本地檔案。

為了解決這件事情,讓WebView可以順利讀取到本地檔案,那就直接在App裏開啟一個HttpServer讓WebView透過HttpServer來讀取檔案。

找了很多套件,試了很多方式,最後才跟Ktor相見恨晚啊!Ktor是什麼呢?這是一個專門為Kotlin打造的網路相關套件,你可以透過Ktor來建立Server端或是Client端的程式。

而且真的算是蠻簡單的,官方也提供了範例程式,基本上只需要小改一下就可以直些使用了。

引入套件//Ktor的核心包implementation"io.ktor:ktor-server-core:2.0.1"//供Ktor使用的引擎包,另外有Jetty,Tomcat,CIO可用implementation"io.ktor:ktor-server-netty:2.0.1"//用於印出Request及Response的log用implementation"io.ktor:ktor-server-call-logging:2.0.1"//用於支援PartialContent用implementation"io.ktor:ktor-server-partial-content: 純粹紀錄一下怕以後忘記,在開發系統App的時候,有時候會需要遠端更新App版本,這時候就可以發揮系統App的優勢,直接拿系統權限下Command把Apk蓋過去做升級的動作,下面勢力:Stringlibs="LD_LIBRARY_PATH=/vendor/lib:/system/lib";String[]commands=newString[]{ libs+"mount-orw,remount/",libs+"chmod666/system/app/{AppName}/{ApkName}.apk",libs+"rm/system/app/{AppName}/{ApkName}.apk",libs+"cp-rf"+{NewApkPath}/{ApkName}.apk/system/app/{AppName}/",libs+"chmod



請為這篇文章評分?