Android 三大存储权限
Android 三大存储权限
在Android13之前使用的两个权限是android.permission.READ_EXTERNAL_STORAGE
和android.permission.WRITE_EXTERNAL_STORAGE
。在Android13之后使用的是如下三个:
- android.permission.READ_MEDIA_IMAGES
- android.permission.READ_MEDIA_AUDIO
- android.permission.READ_MEDIA_VIDEO
推荐使用MediaStore的方式访问媒体文件,而不是传统的文件方式,MediaStore以表的方式进行文件检索,效率更高。
存储图片
@OptIn(markerClass = [UnstableApi::class])
@Composable
internal fun StoreScreen(
modifier: Modifier,
snackBarHostState: SnackbarHostState
) {
val context: Context = LocalContext.current
val coroutineScope: CoroutineScope = rememberCoroutineScope()
val imagePermissions: Array<String> =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
arrayOf(android.Manifest.permission.READ_MEDIA_IMAGES)
} else {
arrayOf(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE)
}
val launcher: ManagedActivityResultLauncher<Array<String>, Map<String, @JvmSuppressWildcards Boolean>> =
rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestMultiplePermissions()
) { map: Map<String, Boolean> ->
val isSuccess: Boolean = map.all { it.value }
coroutineScope.launch {
snackBarHostState.showSnackbar("获取权限${if (isSuccess) "成功" else "失败"}")
}
}
Column(
modifier = modifier.fillMaxSize()
) {
Text(
text = "存储图片",
Modifier
.padding(top = 10.dp)
.background(color = Color.Black, shape = RoundedCornerShape10)
.padding(all = 5.dp)
.clickable {
if (
imagePermissions.any {
ContextCompat.checkSelfPermission(
context,
it
) != PackageManager.PERMISSION_GRANTED
}
) {
launcher.launch(input = imagePermissions)
return@clickable
}
val bitmap: Bitmap = createBitmap(100, 100).applyCanvas {
val paint = Paint()
paint.isAntiAlias = true
paint.color = Color(0xFFE0C2CC).toArgb()
drawRoundRect(0F, 0F, 100F, 100F, 0F, 0F, paint)
paint.color = Purple40.toArgb()
paint.textSize = 12F
drawText("Hello World 世界! ", 0F, paint.textSize, paint)
}
val contentValues: ContentValues = contentValuesOf(
MediaStore.Images.Media.DISPLAY_NAME to "${System.currentTimeMillis()}.png",
MediaStore.Images.Media.MIME_TYPE to MimeTypes.IMAGE_PNG
)
// 存在 /sdcard
context.contentResolver.insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
contentValues
)?.let { uri: Uri ->
Log.i(TAG, "StoreScreen -> uri: $uri")
context.contentResolver.openOutputStream(uri)
?.use { outputStream: OutputStream ->
val isSuccess: Boolean =
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
Log.i(TAG, "StoreScreen -> isSuccess: $isSuccess")
}
}
},
color = Color.White
)
Text(
text = "查看图片",
Modifier
.padding(top = 10.dp)
.background(color = Color.Black, shape = RoundedCornerShape10)
.padding(all = 5.dp)
.clickable {
context.contentResolver.query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
arrayOf(MediaStore.Images.Media._ID),
null,
null,
null
)?.use { cursor: Cursor ->
val idColumn: Int = cursor.getColumnIndex(MediaStore.Images.Media._ID)
while (cursor.moveToNext()) {
val id: Long = cursor.getLong(idColumn)
val contentUri: Uri = ContentUris.withAppendedId(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
id
)
Log.i(TAG, "StoreScreen -> contentUri: $contentUri")
}
}
},
color = Color.White
)
}
}
音频
val contentValues1: ContentValues = contentValuesOf(
MediaStore.Audio.Media.DISPLAY_NAME to "${System.currentTimeMillis()}.wav",
MediaStore.Audio.Media.MIME_TYPE to MimeTypes.AUDIO_WAV
)
context.contentResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, contentValues1)?.let { uri ->
Log.i(TAG, "startRecord -> uri: $uri")
}
视频
val contentValues: ContentValues = contentValuesOf(
MediaStore.Video.Media.DISPLAY_NAME to "${System.currentTimeMillis()}.mp4",
MediaStore.Video.Media.MIME_TYPE to MimeTypes.VIDEO_MP4
)
context.contentResolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, contentValues)?.let { uri ->
Log.i(TAG, "startRecord -> uri: $uri")
}
基本上都是增删改查,大体都是一致的。