Kotlin Android 创建多级目录求助:在Podcasts目录下构建风电场相关文件夹结构
在Android的Podcasts目录下创建多层子文件夹的解决方案
作为Android新手,你遇到的问题核心在于错误的目录路径写法和Android不同版本的存储权限/机制差异,直接硬编码/Podcasts/Fuchsberg/肯定是行不通的——外部存储的根目录不是/,而且Android 10+引入了Scoped Storage,限制了直接访问公共目录的方式。下面一步步帮你解决:
一、修正目录获取逻辑(核心问题)
你不能直接写死路径,得用系统提供的API获取公共Podcasts目录的正确位置,再拼接子文件夹。这里分版本适配:
private fun createFolder(windparkName: String, anlageNumber: String) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // Android 11+ 用MediaStore适配Scoped Storage createFolderForAndroid11Plus(windparkName, anlageNumber) } else { // Android 10及以下用旧方法 val podcastsDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS) // 拼接成你要的结构:Podcasts/风电场名称/风机编号 val targetDir = File(podcastsDir, "$windparkName/$anlageNumber") // mkdirs()会自动创建所有不存在的父目录 val isCreated = targetDir.mkdirs() if (isCreated) { Toast.makeText(this, "目录创建成功:${targetDir.path}", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "目录创建失败,请检查权限或路径", Toast.LENGTH_SHORT).show() } } } // Android 11+ 专属的目录创建方法(适配Scoped Storage) private fun createFolderForAndroid11Plus(windparkName: String, anlageNumber: String) { val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, anlageNumber) // 设置相对路径,指向Podcasts下的子目录 put(MediaStore.MediaColumns.RELATIVE_PATH, "${Environment.DIRECTORY_PODCASTS}/$windparkName") put(MediaStore.MediaColumns.MIME_TYPE, "vnd.android.document/directory") put(MediaStore.MediaColumns.IS_PENDING, 1) } val uri = contentResolver.insert(MediaStore.Files.getContentUri("external"), contentValues) uri?.let { contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0) contentResolver.update(it, contentValues, null, null) Toast.makeText(this, "目录创建成功", Toast.LENGTH_SHORT).show() } ?: run { Toast.makeText(this, "目录创建失败", Toast.LENGTH_SHORT).show() } }
二、处理动态权限申请(必须步骤)
你在Manifest里加了权限,但Android 6.0(API 23)及以上需要动态申请危险权限,否则目录创建会失败。
1. 定义权限相关变量
private val REQUEST_PERMISSION_CODE = 100 // 根据系统版本返回需要申请的权限 private fun getRequiredPermissions(): Array<String> { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // Android 13+ 用细分的媒体权限 arrayOf( Manifest.permission.READ_MEDIA_AUDIO, Manifest.permission.RECORD_AUDIO ) } else { // Android 12及以下用旧的存储权限 arrayOf( Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO ) } }
2. 修改按钮点击逻辑,先检查权限
btnStart.setOnClickListener { val permissions = getRequiredPermissions() // 检查所有权限是否已授权 val allPermissionsGranted = permissions.all { ContextCompat.checkSelfPermission(this, it) == PackageManager.PERMISSION_GRANTED } if (allPermissionsGranted) { // 获取输入的风电场名称和风机编号 val windparkName = etWindpark.text.toString().trim() val anlageNumber = etAnlage.text.toString().trim() if (windparkName.isNotEmpty() && anlageNumber.isNotEmpty()) { createFolder(windparkName, anlageNumber) } else { Toast.makeText(this, "请填写完整的风电场名称和风机编号", Toast.LENGTH_SHORT).show() } } else { // 申请权限 ActivityCompat.requestPermissions(this, permissions, REQUEST_PERMISSION_CODE) } }
3. 处理权限申请结果
override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == REQUEST_PERMISSION_CODE) { val allGranted = grantResults.all { it == PackageManager.PERMISSION_GRANTED } if (allGranted) { val windparkName = etWindpark.text.toString().trim() val anlageNumber = etAnlage.text.toString().trim() if (windparkName.isNotEmpty() && anlageNumber.isNotEmpty()) { createFolder(windparkName, anlageNumber) } else { Toast.makeText(this, "请填写完整的风电场名称和风机编号", Toast.LENGTH_SHORT).show() } } else { Toast.makeText(this, "需要授予存储和录音权限才能创建目录", Toast.LENGTH_SHORT).show() } } }
三、更新Manifest权限(适配Android 13+)
把你原来的权限声明改成下面这样,适配不同版本的权限要求:
<!-- Android 13+ 的音频权限 --> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <!-- Android 12及以下的存储权限 --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- 保持你原来的requestLegacyExternalStorage,适配Android 10 --> <application ... android:requestLegacyExternalStorage="true"> ... </application>
几个关键注意点
- 绝对路径不可靠:永远不要硬编码外部存储路径,用
Environment类的方法获取系统目录。 - Scoped Storage适配:Android 11+限制了直接操作公共目录,必须用MediaStore API来创建文件夹或文件。
- 权限检查要彻底:不同Android版本的权限要求不一样,一定要根据版本动态适配。
内容的提问来源于stack exchange,提问作者DickDaniel67
相关产品推荐
相关产品推荐

