You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 06:55:30