Android API 33:在Downloads/Documents后台创建文件夹替代废弃API
替代废弃API在公共存储创建日志文件夹及写入文件的方案
适配思路
针对API 29+废弃Environment.getExternalStoragePublicDirectory的问题,结合你的企业非应用商店应用场景(后台操作、无需用户选择位置),提供两种可行方案:
方案一:基于MediaStore实现(推荐,符合Scoped Storage规范)
无需申请特殊权限,直接通过MediaStore在Downloads目录下创建子文件夹并写入日志文件,完全适配Android 10+。
代码实现
// 在Downloads目录下创建指定子文件夹 private fun createLogSubFolder(context: Context, folderName: String = "AppLogs"): Uri? { val contentValues = ContentValues().apply { put(MediaStore.Downloads.RELATIVE_PATH, "${Environment.DIRECTORY_DOWNLOADS}/$folderName") put(MediaStore.Downloads.DISPLAY_NAME, folderName) put(MediaStore.Downloads.MIME_TYPE, "vnd.android.document/directory") put(MediaStore.Downloads.IS_DIRECTORY, true) } return context.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) } // 写入日志到子文件夹内的txt文件 fun writeLogFile(context: Context, logContent: String, fileName: String = "log_${System.currentTimeMillis()}.txt") { // 确保文件夹已创建 createLogSubFolder(context) ?: return val contentValues = ContentValues().apply { put(MediaStore.Downloads.RELATIVE_PATH, "${Environment.DIRECTORY_DOWNLOADS}/AppLogs") put(MediaStore.Downloads.DISPLAY_NAME, fileName) put(MediaStore.Downloads.MIME_TYPE, "text/plain") put(MediaStore.Downloads.DATE_ADDED, System.currentTimeMillis() / 1000) } val fileUri = context.contentResolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues) ?: return context.contentResolver.openOutputStream(fileUri)?.use { stream -> stream.write(logContent.toByteArray(Charsets.UTF_8)) } }
权限配置
仅需在AndroidManifest.xml中添加基础权限(API 29+自动授予,无需动态申请):
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
方案二:使用MANAGE_EXTERNAL_STORAGE权限(兼容旧代码逻辑)
如果希望沿用类似旧API的文件操作方式,可申请MANAGE_EXTERNAL_STORAGE权限(Android 11+),获取后即可直接读写公共存储目录,无需修改核心文件操作逻辑。
步骤1:配置权限
在AndroidManifest.xml中添加权限:
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
步骤2:动态请求权限
fun requestFullStorageAccess(context: Context) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (!Environment.isExternalStorageManager()) { val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION).apply { data = Uri.parse("package:${context.packageName}") flags = Intent.FLAG_ACTIVITY_NEW_TASK } context.startActivity(intent) } } }
步骤3:文件操作代码(与旧逻辑几乎一致)
fun createLogFolderAndWrite(logContent: String) { // 检查权限是否已授予 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && !Environment.isExternalStorageManager()) { return } val downloadsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) val logFolder = File(downloadsDir, "AppLogs") if (!logFolder.exists()) { logFolder.mkdirs() } val logFile = File(logFolder, "log_${System.currentTimeMillis()}.txt") logFile.writeText(logContent, Charsets.UTF_8) }
说明
- 该方案需要用户授予"所有文件访问权限",但企业应用可提前完成权限配置,后台操作不受影响。
- Android 10(API 29)可通过在
application标签添加android:requestLegacyExternalStorage="true"临时兼容旧API,无需申请特殊权限。
额外注意
- 日志文件名建议添加时间戳,避免文件覆盖。
- 后台操作时,需确保权限已提前获取(MediaStore方案无需额外操作,MANAGE_EXTERNAL_STORAGE需先完成权限请求)。
内容的提问来源于stack exchange,提问作者usr30911
相关产品推荐
相关产品推荐

