Android 三大存储权限

Android 三大存储权限

在Android13之前使用的两个权限是android.permission.READ_EXTERNAL_STORAGEandroid.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")
}

基本上都是增删改查,大体都是一致的。

posted @ 2025-07-06 11:59  爱情丶眨眼而去  阅读(38)  评论(0)    收藏  举报