Котлин — Создайте каталог в среде.getExternalStorageDirectory().getAbsolutePath() на Android 10

#android #kotlin #directory #storage #mkdir

Вопрос:

Об этом уже спрашивали раньше, но я не могу найти ответ, который хорошо подходит для меня.

У меня есть приложение, в котором при первом запуске мне нужно создать несколько каталогов и файлов в этих каталогах.

То, что я сделал до сих пор, хорошо работает на Android с 5 по 9. К моему удивлению, он не работает на Android 10 , так как getExternalStorageDirectory() устарел.

Это то, что я делаю до сих пор:

 if(isFirstRun)
{
    Log.i("MyApp","ServerWizardActivity > createNecesaryFoldersFiles > isFirstRun")

    val absoluteExternalStorageDirectory = Environment.getExternalStorageDirectory().getAbsolutePath()
    val appDirectory = File("${absoluteExternalStorageDirectory}/MyApp/")
    val picturesDirectory = File("${absoluteExternalStorageDirectory}/Pictures/")

    val fullPath: String? = "${absoluteExternalStorageDirectory}/MyApp/${jsonFileName}"
    var dataFile = File(fullPath)

    if (!appDirectory .exists())
    {
        Log.i("MyApp","ServerWizardActivity > createNecesaryFoldersFiles > appDirectory")

        appDirectory .mkdir()
    }

    if (!picturesDirectory.exists())
    {
        Log.i("MyApp","ServerWizardActivity > createNecesaryFoldersFiles > picturesDirectory")

        picturesDirectory.mkdir()
    }

    if(!dataFile.exists())
    {
        Log.i("MyApp","ServerWizardActivity > createNecesaryFoldersFiles > dataFile")

        dataFile.createNewFile()
    }
}
else
{
    Log.i("MyApp","ServerWizardActivity > createNecesaryFoldersFiles > isNotFirstRun")
}
 

Пока все хорошо.
Однако мне нужно создать каталог непосредственно в хранилище устройства, на том же уровне, что и DCIM, Изображения, документы и загрузки, чтобы я мог согласовать его с версией Android 5-9 и версией приложения iOS.

Я попробовал приведенный ниже код для Android 10 , и он работает, но я могу создать каталог только в одном из каталогов в среде (в данном случае каталог изображений). Есть ли какой-нибудь способ обойти это? Я не хочу менять всю структуру приложения из-за этого.

 if(isFirstRun)
    {
        val resolver = contentResolver
        val contentValues = ContentValues()
        contentValues.put(
            MediaStore.MediaColumns.RELATIVE_PATH,
            Environment.DIRECTORY_PICTURES   "/MyApp"
        )
        val path = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues).toString()
        val folder = File(path)
        val isCreated = folder.exists()

        if (!isCreated)
        {
            folder.mkdirs()
        }
    }
 

О, и до сих пор они есть у меня в файле манифеста Android:

 <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

<application
    android:requestLegacyExternalStorage="true"
</application>
 

Комментарии:

1. Возможно, вы могли бы использовать ACTION_OPEN_DOCUMENT_TREE и позволить пользователю решить, где на устройстве пользователя (или в выбранном пользователем поставщике облачных хранилищ) ваше приложение должно хранить контент пользователя .

2. @CommonsWare — Это не мое дело. И поскольку приложение должно работать так же, как версия iOS, мне пришлось бы изменить и эту версию, а это не вариант.

3. Пользователь, находящийся вне вашего приложения, может создать каталог из корневого каталога внешнего хранилища, но у пользователя нет другого способа (кроме как через ACTION_OPEN_DOCUMENT_TREE ) предоставить вам доступ к этому каталогу. Если ваше приложение будет распространяться через Play Store, ваш android:requestLegacyExternalStorage="true" подход не будет работать, начиная с конца этого года, и весьма маловероятно, что Google предоставит вам отказ от использования MANAGE_EXTERNAL_STORAGE .

4. @CommonsWare Спасибо за информацию. Не уверен, что приложение будет проходить через PlayStore (версия для iOS размещена в доме). Я попытаюсь понять, как я могу изменить структуру, не слишком влияя на функциональность.

Ответ №1:

Попробуйте использовать следующий код, который поможет вам создать тестовый каталог внутри каталога документов.

 //Make sure don't forget to ask runtime permission of read/write file
private fun writeFile(fileData: String, fileName: String) {
val dir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    File(
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)
            .toString()   "/"   "test"
    )
} else {
    File(
        Environment.getExternalStorageDirectory()
            .toString()   "/${Environment.DIRECTORY_DOCUMENTS}/"   "test"
    )
}
dir.apply {
    if (!this.exists()) this.mkdir()
    File(this, "$fileName.txt").apply {
        if (!this.exists()) this.createNewFile()
        FileOutputStream(this).apply {
            write(fileData.toByteArray())
            close()
        }
    }
}
}
 

Комментарии:

1. Спасибо. Я попробую это сделать. Проблема в том, что мне нужен каталог, созданный на уровне выше, на том же уровне, что и DIRECTORY_DOCUMENTS, а не внутри него. Поначалу все приложение было разработано именно так.

2. К сожалению, я продолжаю получать эту ошибку: «java.io.IOException: Нет такого файла или каталога» при запуске приложения на моем телефоне. И по какой-то причине я не могу создать A10 AVDs, чтобы протестировать его, так как я продолжаю запускать BSOD…