Как я могу предоставить разрешение на хранение в DIRECTORY_DOWNLOADS при работе на Android 10?

#android #android-permissions #android-10.0 #android-external-storage

Вопрос:

Моя проблема

У меня есть приложение для Android, предназначенное для API 30, которое хранит файл данных в общедоступном каталоге DIRECTORY_DOWNLOADS, чтобы пользователи могли получить доступ и скопировать его в свой собственный каталог в другом месте.

Это работает для API 28 и ниже, а также для API 30, но не для API 29, доступ к которому запрещен.

Что я сделал до сих пор

Я изучил различные сообщения здесь, в том числе ответы опытных пользователей, которые ответили на темы API 29 / Android 10, и соответствующие темы для разработчиков Android, но не смогли найти конкретный ответ на мою проблему (или тот, который я могу понять). Некоторые примеры, которые я видел, находятся в Котлине — я знаком с Java, но не с Котлином.

Что у меня есть в моем коде

В моем манифесте:

     <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 

(Первый и третий предназначены для других функций моего приложения.)

В сборке.gradle:

     compileSdkVersion 30
    defaultConfig {
        applicationId "com.enborne.spine"
        minSdkVersion 19
        targetSdkVersion 30
        versionCode 29
        versionName "3.0"
        signingConfig signingConfigs.config
    }
 

В моем Java-коде:

Мой основной позвоночник класса включает в себя следующие глобальные переменные:

     public static File      dataDir;
    public static File      fileSaveDir;
 

В других модулях у меня есть:

             Spine.dataDir = getFilesDir();
            Spine.fileSaveDir =
                    Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
 

и код, который сохраняет файл:

                     // Default toast text
                    toastText = "Failed to save file";

                    // [code to build fileName snipped]

                    // Create the output file
                    outFile = new File(Spine.fileSaveDir, fileName);
                    
                    try
                    {
                        out = new BufferedWriter(new FileWriter(outFile), 1024);
                    
                        // Output the formatted body
                        // [code snipped]

                        // Flush and close file
                        out.flush();
                        out.close();

                        // File has been saved
                        toastText = "File saved as "   formatDesc;
                    }
                    catch (IOException e)
                    {
                        toastText  = "n"   e.getMessage();
                    }
 

При запуске на API 29 исключение создается при попытке создать BufferedWriter. В противном случае на других уровнях API все работает. Я повсюду искал решение этой проблемы, но не смог его найти.

Фактический вывод текста тостов при запуске на эмуляторе API 29 в Android Studio выглядит следующим образом:

 Failed to save file
/storage/emulated/0/Download/
20210520.txt: open failed : EACCES
(Permission denied)
 

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

1. Запросите устаревшее внешнее хранилище в файле манифеста.

2. В данный момент я нахожусь вдали от своего компьютера (здесь уже очень поздно), но раньше это было у меня в манифесте, и я почти уверен, что это не решило проблему. Не имело значения, нацелился ли я на API 29 или API 30. В любом случае, я проверю еще раз завтра.

3. Отсортировано — пожалуйста, смотрите мой ответ и мои комментарии в ответе Шазника.

Ответ №1:

 <application
        android:requestLegacyExternalStorage="true"
    ....
 

Добавьте это в свой манифест Android.

Приложения, работающие на Android 11, но предназначенные для Android 10 (уровень API 29), все еще могут запрашивать атрибут requestLegacyExternalStorage. Этот флаг позволяет приложениям временно отказаться от изменений, связанных с областью хранения, таких как предоставление доступа к различным каталогам и различным типам медиафайлов. После обновления приложения для Android 11 система игнорирует флаг requestLegacyExternalStorage.

Если ваше приложение отказывается от хранилища с областью действия при работе на устройствах Android 10, рекомендуется по-прежнему устанавливать значение requestLegacyExternalStorage равным true в файле манифеста вашего приложения. Таким образом, ваше приложение может продолжать вести себя так, как ожидалось, на устройствах под управлением Android 10.

Источник: https://developer.android.com/about/versions/11/privacy/storage

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

1. Спасибо. Да, я тоже это читал, но, к сожалению, это не помогло.

2. Поскольку targetSdkVersion 30 атрибут requestLegacyExternalStorage не будет работать. Попробуйте подтвердить это: если вы измените его на targetSdkVersion 29 , то я думаю, что это сработает.

3. как говорит @ecle. Я не знаю, связано ли это, но в вашем файле build.gradle указано, что targetSdkVersion 30-возможно, вы захотите это изменить, и я предполагаю, что вы установили 28 или меньше в своем манифесте, потому что в вашем вопросе говорится: «Это работает для api 28 и ниже».

4. Нет, вы предполагаете неправильно. С целевым Sdk, установленным на 30, он работает для всех версий, кроме 29.

5. Номер цели 30 и запрос устаревшего внешнего хранилища работает для устройства Android 10.

Ответ №2:

Решение (спасибо тем, кто откликнулся) состоит в том, чтобы добавить android:requestLegacyExternalStorage=»true» в раздел приложения манифеста, по-прежнему ориентируясь на API 30.

Во всех моих тестированиях это было в неправильном разделе манифеста, поэтому я не мог заставить его работать.

Ответ №3:

Некоторое время вам требуется разрешение среды выполнения для записи во внешнее хранилище.

 if (ContextCompat.checkSelfPermission(Main_activity.this),
                Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {
            if (!ActivityCompat.shouldShowRequestPermissionRationale(OMain_activity.this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                ActivityCompat.requestPermissions(getActivity(),
                        new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                        1);
            }
    }
 

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

1. У меня уже есть очень похожий код в функции onCreate моей основной деятельности, за исключением того, что он объединяет запросы как для WRITE_EXTERNAL_STORAGE, так и для ACCESS_FINE_LOCATION.

2. Ооо, вы не добавили этот код, поэтому я считаю, что в этом была проблема.

3. Код, который я процитировал, имел отношение только к API 29 (конкретно), который не работал. Без кода, который вы здесь упомянули, ничего из этого не сработало бы! 🙂 В любом случае, спасибо, что уделили мне время.

4. Хорошо, никаких проблем.