Android分区存储
1、分區(qū)存儲概述
分區(qū)存儲是Android 10開始引進的Android系統(tǒng)存儲管理機制,它允許App讀取和寫入App自身創(chuàng)建的文件而不需要任何存儲權限。其中根據(jù)存儲位置的不同,可以分為內(nèi)部內(nèi)部存儲和外部存儲。內(nèi)部存儲就不用多說了,而外部存儲又分為私有空間和公共空間。私有存儲空間位置是/sdcard/Android/data/包名,而公共空間則是相冊、下載等。對我們開發(fā)者影響最大的就是對于公共存儲空間的讀寫了,總結如下:
Android分區(qū)存儲機制其實挺好的,讓很多軟件不能為所欲為,至少提高了“犯罪成本”。就是來得晚了一些,導致我們開發(fā)者要各種兼容…
2、讀取
以讀取相冊(DCIM)為例
val uris = ArrayList<Uri>() contentResolver.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null,null, "${MediaStore.MediaColumns.DATE_ADDED} desc")?.use {// 這里的it是一個Cursorwhile (it.moveToNext()) {val id = it.getLong(it.getColumnIndexOrThrow(MediaStore.MediaColumns._ID))val uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)uris.add(uri)} }這個contextResolver就是是Context.getContentResolver(),Actvity是Context子類,所以用kotlin就可以直接簡寫。
這樣就拿到了相冊里面的圖片的Uri,它是“content://”形式的uri:
這種讀取形式在低版本也是可用的,但是需要READ權限。在Android 10和11中,如果有READ權限,則可以讀取到所有的圖片文件的Uri,否則只能讀取到App本身創(chuàng)建的文件Uri。
在拿到Uri后,我們可以通過流的形式讀取它:
如果是用于展示圖片,可以使用Glide等開源框架,它們本身就支持加載Uri。
3、寫入
同樣以寫入相冊(DCIM)為例:
val bitmap = BitmapFactory.decodeResource(resources, R.drawable.girl) val values = ContentValues() values.put(MediaStore.MediaColumns.DISPLAY_NAME, "test.png") values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png") if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // RELATIVE_PATH需要API 29values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM) } else {values.put(MediaStore.MediaColumns.DATA, "${Environment.getExternalStorageDirectory().path}/${Environment.DIRECTORY_DCIM}/test.png") } contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)?.also { uri ->contentResolver.openOutputStream(uri)?.use { os ->bitmap.compress(Bitmap.CompressFormat.PNG, 100, os)} }同樣地,在低版本寫入需要WRITE權限,在10和11上不需要。但是如果這個要修改其他App創(chuàng)建的文件,就需要寫成這樣
private var uri: Uri? = null // 通過某些操作獲取這個uri并賦值 private fun change() {val temp = uri ?: returncontentResolver.openInputStream(temp)?.use { val bitmap = BitmapFactory.decodeStream(it)val values = ContentValues()values.put(MediaStore.MediaColumns.DISPLAY_NAME, "test.png")values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png")if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // RELATIVE_PATH需要API 29values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)} else {values.put(MediaStore.MediaColumns.DATA, "${Environment.getExternalStorageDirectory().path}/${Environment.DIRECTORY_DCIM}/test.png")}try {contentResolver.openOutputStream(temp)?.use { os ->bitmap.compress(Bitmap.CompressFormat.PNG, 80, os)Toast.makeText(this@MainActivity, "OK", Toast.LENGTH_SHORT).show()}} catch (e: Exception) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && e is RecoverableSecurityException) {startIntentSenderForResult(e.userAction.actionIntent.intentSender, 10086, null, 0, 0, 0)} else {throw e}}} }override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {super.onActivityResult(requestCode, resultCode, data)if(requestCode == 10086 && resultCode == RESULT_OK) {change()} }會彈出這樣的彈窗
如果是對多個文件進行寫入,在Android 11上可以這樣寫:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {val request = MediaStore.createWriteRequest(contentResolver, listOf(uri1, uri2))startIntentSenderForResult(request.intentSender, 10086, null, 0, 0, 0) }值得一提的是,用戶如果卸載了App后再重新安裝,即使是卸載前App自身創(chuàng)建的文件也需要相關權限。也就是說卸載重裝之后,“同一個App”其實在系統(tǒng)眼里不是同一個App。
3、管理存儲的權限
分區(qū)存儲機制很好地規(guī)范了Android App的存儲行為,讓它們讀自己該讀的,寫自己該寫的。但是有的應用天生就需要對SD卡進行全方位的訪問,比如各種文件瀏覽器、垃圾清理軟件等等,雖然很多所謂的垃圾清理軟件本身就是最該被清理的垃圾…對此,Android 11引入了一個新的權限:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />有了這個權限,就可以跟以前的版本一樣隨意玩耍了。那么是不是可以直接申請這個權限就可以了呢?機智如我,是可以的,不過應用市場不讓上架…所以大部分App是不允許使用這個權限的。如果要申請此權限,需要打開設置界面,讓用戶手動設置
val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) startActivityForResult(intent, 10010)如果在manifest中添加了requestLegacyExternalStorage屬性,還可以加上包名
val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION) intent.data = Uri.parse("package:$packageName") startActivityForResult(intent, 10010)出現(xiàn)的界面長這樣
總結
以上是生活随笔為你收集整理的Android分区存储的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaWeb QQ邮箱找回密码
- 下一篇: 用计算机时按错了按什么键恢复出厂设置,电