Универсальный способ записи на внешнюю SD карту на Android

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

Я читал везде, что Android не позволяет пользователям писать на внешнюю SD-карту, под SD-картой я имею в виду внешнюю и монтируемую SD-карту и не внешнее хранилище, но приложения файлового менеджера управляют записью на внешний SD на всех Android версии.

каков лучший способ предоставить доступ для чтения / записи на внешнюю SD-карту на разных уровнях API (Pre-KitKat, KitKat, Lollipop+)?

обновление 1

я попробовал метод 1 из ответа Doomknight, безрезультатно: Как вы можете видеть, я проверяю разрешения во время выполнения, Прежде чем пытаться писать на SD:

HashSet<String> extDirs = getStorageDirectories();
for(String dir: extDirs) {
    Log.e("SD",dir);
    File f = new File(new File(dir),"TEST.TXT");
    try {
        if(ActivityCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED) {
            f.createNewFile();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

но я получаю ошибку доступа, пробовал на двух разных устройствах: HTC10 и Shield K1.

10-22 14:52:57.329 30280-30280/? E/SD: /mnt/media_rw/F38E-14F8
10-22 14:52:57.329 30280-30280/? W/System.err: java.io.IOException: open failed: EACCES (Permission denied)
10-22 14:52:57.329 30280-30280/? W/System.err:     at java.io.File.createNewFile(File.java:939)
10-22 14:52:57.329 30280-30280/? W/System.err:     at com.myapp.activities.TestActivity.onResume(TestActivity.java:167)
10-22 14:52:57.329 30280-30280/? W/System.err:     at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1326)
10-22 14:52:57.330 30280-30280/? W/System.err:     at android.app.Activity.performResume(Activity.java:6338)
10-22 14:52:57.330 30280-30280/? W/System.err:     at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3336)
10-22 14:52:57.330 30280-30280/? W/System.err:     at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3384)
10-22 14:52:57.330 30280-30280/? W/System.err:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2574)
10-22 14:52:57.330 30280-30280/? W/System.err:     at android.app.ActivityThread.access0(ActivityThread.java:150)
10-22 14:52:57.330 30280-30280/? W/System.err:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1399)
10-22 14:52:57.330 30280-30280/? W/System.err:     at android.os.Handler.dispatchMessage(Handler.java:102)
10-22 14:52:57.330 30280-30280/? W/System.err:     at android.os.Looper.loop(Looper.java:168)
10-22 14:52:57.330 30280-30280/? W/System.err:     at android.app.ActivityThread.main(ActivityThread.java:5885)
10-22 14:52:57.330 30280-30280/? W/System.err:     at java.lang.reflect.Method.invoke(Native Method)
10-22 14:52:57.330 30280-30280/? W/System.err:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:819)
10-22 14:52:57.330 30280-30280/? W/System.err:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:709)
10-22 14:52:57.330 30280-30280/? W/System.err: Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
10-22 14:52:57.330 30280-30280/? W/System.err:     at libcore.io.Posix.open(Native Method)
10-22 14:52:57.330 30280-30280/? W/System.err:     at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
10-22 14:52:57.330 30280-30280/? W/System.err:     at java.io.File.createNewFile(File.java:932)
10-22 14:52:57.330 30280-30280/? W/System.err:  ... 14 more

8 ответов


короткий ответ.

ContextCompat.getExternalFilesDirs разрешает Ошибка доступа когда вам не нужно обмениваться файлами.

безопасный способ обмена это использовать контент-провайдер или новая структура доступа к хранилищу.


резюме.

нет универсальный способ to запись на внешний SD карты на Android из-за непрерывные изменения:

  • Pre-KitKat: официальная платформа Android не поддерживает SD-карты вообще, за исключением исключений.

  • KitKat: введены API, которые позволяют приложениям получать доступ к файлам в каталогах приложений на SD-картах.

  • Lollipop: добавлены API, позволяющие приложениям запрашивать доступ к папкам, принадлежащим другим услуги поставщиков.

  • Нуга: предоставляется упрощенный API для доступа к общим каталогам внешнего хранилища.

на основе ответ Doomsknight и шахты и Дейв Смит и Марк Мерфи сообщения в блоге: 1, 2, 3:


1. разрешения.

вы можете предоставить доступ для чтения/записи на внешнюю SD-карту на различные уровни api (API23+ во время выполнения).

с КитКат, разрешения не надо если вы используете каталоги приложений,требуются иначе:

С Kitkat ваши шансы на" полное решение " без укоренения почти ноль: проект Android наверняка облажался. Приложения не получают полный доступ к внешней SD-карте:

  • файловые менеджеры: вы не можете использовать внешнюю SD-карту. В наиболее территории, они могут только читать но не писать.
  • мультимедиа приложения: вы не можете retag / повторно организовать коллекцию мультимедиа больше, как эти приложения не могу ему написать.
  • офисные приложения: почти то же

единственное место 3 rd party apps разрешено писать на вашем внешняя карта - это "собственные каталоги" (т. е. /sdcard/Android/data/<package_name_of_the_app>).

единственный способ действительно исправить это требует от производителя (некоторые из них исправлены это, например, Huawei с их обновлением Kitkat для P6) - или root... (объяснение Иззи продолжается здесь)


2. об универсальном решении.

на история говорит, что нет универсальный способ записи на внешнюю SD-карту но продолжает...

этот факт - это демонстрируемые эти примеры конфигураций внешнего хранилища для устройств.

доступ к внешнему хранилищу защищен различными Android разрешения. Начиная с Android 1.0, доступ на запись защищен the WRITE_EXTERNAL_STORAGE разрешение. Начиная с Android 4.1, прочитайте доступ защищен с помощью READ_EXTERNAL_STORAGE разрешения.

начиная с Android 4.4, владелец, группа и режимы файлов на внешние запоминающие устройства теперь синтезируются на основе каталога структура. Это позволяет приложениям управлять своими пакетами каталоги на внешнем хранилище, не требуя, чтобы они содержали широкий WRITE_EXTERNAL_STORAGE разрешения. Например, приложение с пакетом имя com.example.foo теперь можно свободно открыть Android/data/com.example.foo/ на внешних запоминающих устройствах без разрешения. Эти синтезированные разрешения выполняются упаковка устройства хранения raw в Демоне предохранителей.

Android 6.0 представляет новый во время выполнения разрешения модели, где приложений запрашивать возможности при необходимости во время выполнения. Потому что новая модель включает в себя READ/WRITE_EXTERNAL_STORAGE разрешения, платформа нужно динамически предоставлять доступ к хранилищу без убийства или перезапуск уже запущенных приложений. Он делает это, поддерживая три различные виды всех установленных устройств хранения данных:

  • /mnt/runtime / default отображается для приложений без специального хранилища разрешения...
  • /mnt/runtime / read отображается в приложениях с Панель
  • /mnt/runtime / write отображается в приложениях с Все

3. о обновление 1.

я бы использовал каталоги конкретных приложений чтобы избежать этой проблемы вашего обновленного вопроса и ContextCompat.getExternalFilesDirs() используя документация getExternalFilesDir в качестве ссылки.

улучшение эвристика чтобы определить, что представляет собой съемный носитель на основе различных уровней api, таких как android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT

помните, что Android 6.0 поддерживает портативные устройства хранения и сторонние приложения должны пройти через Рамки Доступа К Хранилищу. Устройства HTC10 и Щит К1 несколько вероятно, API 23.

ваш отчет показывает доступ запрещен исключение ссылке /mnt/media_rw, как исправления для API 19+:

<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
<group gid="sdcard_r" />
<group gid="sdcard_rw" />
<group gid="media_rw" /> // this line is added via root in the link to fix it.
</permission>

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

возможно Gizm0 это в ваш getStorageDirectories() способ это хорошая отправная точка.

ContextCompat.getExternalFilesDirs разрешает вопрос если вам не нужен доступ к другим папкам.


4. Запрос разрешений в манифесте (Api во время (Api >= 23).

добавьте следующий код в свой AndroidManifest.xml и читать получение доступа к внешнему хранилищу

для того, чтобы ... запись файлов на внешнее хранилище, ваше приложение должно приобретать... система permissions:

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

Если вам нужно оба... нужно запросить только WRITE_EXTERNAL_STORAGE разрешения.

игнорировать следующую заметку из-за ошибок, но попробуйте использовать ContextCompat.getExternalFilesDirs():

Примечание: начиная с Android 4.4, эти разрешения не требуется если Вы читаете или пишете только те файлы, которые принадлежат приложение. Для дополнительной информации... см. сохранение файлов, являются app-private.

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
                     android:maxSdkVersion="18" />
</manifest>

разрешения на запрос во время выполнения, если уровень API 23+ и читать запрос разрешений во время выполнения

начиная с Android 6.0 (уровень API 23), пользователи предоставляют разрешения приложения во время работы приложения, а не при установке приложения ... или обновить приложение ... пользователь может отозвать разрешения.

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_EXTERNAL_STORAGE);

5. До КитКат попробуйте использовать Doomsknight метод 1, Метод 2 в противном случае.

читать объяснение Марка Мерфи и рекомендовано Диана Hackborn и Дейв Смит ones

  • до Android 4.4 не было официальной поддержки съемных носителей в Android, Начиная с KitKat, концепция "первичного" и "вторичного" внешнего хранилища появляется в API FMW.
  • предыдущие приложения просто полагаются на индексацию MediaStore, поставляются с оборудованием или исследуют точки монтирования и применяют некоторые эвристики для определения того, что представляет собой съемный носитель.
  • начиная с Android 4.2, был запрос от Google для производителей устройств, чтобы заблокировать съемные носители для обеспечения безопасности (многопользовательская поддержка) и новые тесты были добавлены в 4.4.
  • Так Как Спам getExternalFilesDirs() и другие методы были добавлены, чтобы вернуть полезный путь на всех доступных томах хранения (первый возвращаемый элемент является основным Томом).
  • в таблице ниже показано, что разработчик может попытаться сделать и как KitKat ответит: enter image description here

до KitKat попробуйте использовать Doomsknight метод 1 или читать этот ответ Gnathonic или суть:

public static HashSet<String> getExternalMounts() {
    final HashSet<String> out = new HashSet<String>();
    String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
    String s = "";
    try {
        final Process process = new ProcessBuilder().command("mount")
                .redirectErrorStream(true).start();
        process.waitFor();
        final InputStream is = process.getInputStream();
        final byte[] buffer = new byte[1024];
        while (is.read(buffer) != -1) {
            s = s + new String(buffer);
        }
        is.close();
    } catch (final Exception e) {
        e.printStackTrace();
    }

    // parse output
    final String[] lines = s.split("\n");
    for (String line : lines) {
        if (!line.toLowerCase(Locale.US).contains("asec")) {
            if (line.matches(reg)) {
                String[] parts = line.split(" ");
                for (String part : parts) {
                    if (part.startsWith("/"))
                        if (!part.toLowerCase(Locale.US).contains("vold"))
                            out.add(part);
                }
            }
        }
    }
    return out;
}

Читайте также объяснение Паоло Ровелли и попробуйте использовать решение Джеффа Шарки начиная с KitKat:

в KitKat теперь есть публичный API для взаимодействия с эти вторичные общие устройства хранения.

новая Context.getExternalFilesDirs() и Context.getExternalCacheDirs() методы могут возвращать несколько путей, включая как первичные, так и вторичные приборы.

затем вы можете повторить над ними и проверьте Environment.getStorageState() и File.getFreeSpace() определить лучшее место для хранения ваших файлов.

эти методы также доступны на ContextCompat в библиотеке support-v4.


6. Lollipop также ввел изменения и DocumentFile вспомогательный класс.

getStorageState добавлено в API 19, устарело в API 21, использовать getExternalStorageState(File)

вот отличный учебник для взаимодействия с доступом к хранилищу Рамки в KitKat.

взаимодействие с новыми API в Lollipop очень похоже (объяснение Джеффа Шарки).


7. Android 7.0 предоставляет упрощенный API для доступа к внешнему хранилищу dirs.

Уровня Доступа К Каталогам В Android 7.0 приложения могут использовать новые API для запроса доступа к специфический внешний накопитель каталоги, включая каталоги на съемных носителях например, SD-карты...

для получения дополнительной информации см. обучение доступу к каталогу.


8. Android O изменения.

запуск в Android O, в Платформа доступа к хранилищу позволяет таможенные документы поставщики создание поисковых файловых дескрипторов для файлы, находящиеся в удаленном источник данных...

разрешения, до Android O, если приложение запросило разрешение во время выполнения и разрешение было предоставлено, система также неправильно предоставила приложение остальные разрешения, которые принадлежали к тому же группа разрешений, и они были зарегистрированы в манифесте.

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

например, READ_EXTERNAL_STORAGE и WRITE_EXTERNAL_STORAGE...


9. Вопросы и рекомендуемые ответы.

как я могу получить внешний путь SD-карты для Android 4.0+?

функция mkdir() работает пока внутри внутренней флэш-памяти, но не SD-карты?

разница между getExternalFilesDir и getExternalStorageDirectory ()

почему getExternalFilesDirs () не работает на некоторых устройствах?

как использовать новый API доступа к SD-карте, представленный для Android 5.0 (Lollipop)

писать на внешнюю SD карту в Android 5.0 и выше

Android Разрешение на запись SD-карты с помощью SAF (Storage Access Framework)

SAFFAQ: платформа доступа к хранилищу FAQ


10. связанные ошибки и проблемы.

ошибка: на Android 6, при использовании getExternalFilesDirs, он не позволит вам создавать новые файлы в его результатах

запись в каталог, возвращаемый getExternalCacheDir () на Леденец не удается без записи разрешите!--38-->


Я считаю, что есть два способа достичь этого:

метод 1: (не не работать на 6.0 и выше, из-за изменения разрешения)

я использую этот метод в течение многих лет на многих версиях устройств без проблем. кредит из-за исходного источника, так как это не я его написал.

вернет все установленные средства массовой информации (включая реальные карты СД) в списке расположения каталогов строк. Со списком вы можете спросить пользователя, где сохранить и т. д.

можно назвать следующие:

 HashSet<String> extDirs = getStorageDirectories();

способ:

/**
 * Returns all the possible SDCard directories
 */
public static HashSet<String> getStorageDirectories() {
    final HashSet<String> out = new HashSet<String>();
    String reg = "(?i).*vold.*(vfat|ntfs|exfat|fat32|ext3|ext4).*rw.*";
    String s = "";
    try {
        final Process process = new ProcessBuilder().command("mount")
                .redirectErrorStream(true).start();
        process.waitFor();
        final InputStream is = process.getInputStream();
        final byte[] buffer = new byte[1024];
        while (is.read(buffer) != -1) {
            s = s + new String(buffer);
        }
        is.close();
    } catch (final Exception e) {
        e.printStackTrace();
    }

    // parse output
    final String[] lines = s.split("\n");
    for (String line : lines) {
        if (!line.toLowerCase().contains("asec")) {
            if (line.matches(reg)) {
                String[] parts = line.split(" ");
                for (String part : parts) {
                    if (part.startsWith("/"))
                        if (!part.toLowerCase().contains("vold"))
                            out.add(part);
                }
            }
        }
    }
    return out;
}

Метод 2:

используйте библиотеку поддержки v4

import android.support.v4.content.ContextCompat;

просто позвоните следующим, чтобы получить список File местах хранения.

 File[] list = ContextCompat.getExternalFilesDirs(myContext, null);

однако местоположения отличаются по использованию.

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

внешние запоминающие устройства, возвращаемые здесь, считаются постоянной частью устройство, включающее как эмулируемую внешнюю память, так и физический носитель слоты, такие как SD-карты в батарейном отсеке. Возвращаемые пути do не включать переходные устройства, такие как USB-накопители.

приложение может хранить данные на любом или всех возвращенных устройствах. Для например, приложение может выбрать для хранения больших файлов на устройстве с самое доступное пространство

дополнительная информация о ContextCompat

они похожи на приложения конкретных файлов. Скрыто от других приложений.


просто еще один ответ. Этот ответ показывает только 5.0+, потому что я считаю, что ответ Doomknight, размещенный здесь, - лучший способ сделать для Android 4.4 и ниже.

это первоначально опубликовано здесь (есть ли способ получить размер SD-карты в Android?) со мной, чтобы получить размер внешней SD карты на Android 5.0+

чтобы получить внешнюю SD-карту в качестве File:

public File getExternalSdCard() {
    File externalStorage = null;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        File storage = new File("/storage");

        if(storage.exists()) {
            File[] files = storage.listFiles();

            for (File file : files) {
                if (file.exists()) {
                    try {
                        if (Environment.isExternalStorageRemovable(file)) {
                            externalStorage = file;
                            break;
                        }
                    } catch (Exception e) {
                        Log.e("TAG", e.toString());
                    }
                }
            }
        }
    } else {
        // do one of many old methods
        // I believe Doomsknight's method is the best option here
    }

    return externalStorage;
}

Примечание: я получаю только" первую " внешнюю sd-карту, однако вы можете изменить ее и вернуть ArrayList<File> вместо File и пусть цикл продолжается вместо вызова break после того, как первый найден.


в дополнение ко всем другим хорошим ответам, я мог бы добавить немного больше к этому вопросу, чтобы он мог дать более широкий охват для читателей. В моем ответе здесь я бы использовал 2 счетных ресурса для представления внешнего хранилища.

первый ресурс от Android Программирование, большой ботаник ранчо руководство 2-е издание, Глава 16, страница 294.

в книге описаны основные и внешние методы файлов и каталогов. Я попытаюсь сделать резюме того, что могло бы быть отношение к вашему вопросу.

следующая часть из книги:

Внешний Накопитель

Ваша фотография больше, чем место на экране. Полноразмерные изображения слишком большие для того чтобы вставить внутри База данных SQLite, тем более Intent. Им понадобится место для проживания в файловой системе вашего устройства. Обычно вы кладете их в личное хранилище. Вспомните, что вы использовали личное хранилище чтобы сохранить базу данных SQLite. С такими методами, как Context.getFileStreamPath(String) и Context.getFilesDir(), вы можете сделать то же самое с обычными файлами (которые будут жить в подпапка рядом с подпапкой базы данных ваша база данных SQLite живет в)

основные методы файлов и каталогов в контекст

| Method                                                                                |
|---------------------------------------------------------------------------------------|
|File etFilesDir()                                                                      |
| - Returns a handle to the directory for private application files.                    |
|                                                                                       |
|FileInputStream openFileInput(String name)                                             |
| - Opens an existing file for input (relative to the files directory).                 |
|                                                                                       |
|FileOutputStream openFileOutput(String name, int mode)                                 |
| - Opens a file for output, possibly creating it (relative to the files directory).    |
|                                                                                       |
|File getDir(String name, int mode)                                                     |
| - Gets (and possibly creates) a subdirectory within the files directory.              |
|                                                                                       |
|String[] fileList()                                                                    |
| - Gets a list of file names in the main files directory, such as for use with         |
|   openFileInput(String).                                                              |
|                                                                                       |
|File getCacheDir()                                                                     |
| - Returns a handle to a directory you can use specifically for storing cache files.   |
|   You should take care to keep this directory tidy and use as little space as possible|

если вы храните файлы, которые только ваше текущее приложение должно использовать, эти методы именно то, что тебе нужно.

С другой стороны, если вам нужно другое приложение для записи в эти файлы, вы не повезло: пока есть Context.MODE_WORLD_READABLE флаг вы можете передать в openFileOutput(String, int), это устаревший и не полностью надежный в своем воздействии на новые устройства. Если вы храните файлы для обмена с другими приложениями или получения файлов из других приложений (файлы, такие как сохраненные фотографии), нужно хранить их на вместо этого внешнее хранилище.

существует два вида внешнего хранилища: первичное и все остальное. Все устройства Android имеют как минимум одно место для внешнего хранилища: основное расположение, которое находится в папке возвращаемых Environment.getExternalStorageDirectory(). Это может быть SD-карта, но в настоящее время это больше обыкновенно интегрированный в прибор сам. Некоторые устройства могут иметь дополнительную внешнюю память. Что попадет под "все остальное"."

Context предоставляет довольно много методов для получения внешнего хранилища. Эти методы обеспечивают легкий способы доступа к вашему основному внешнему хранилищу и, своего рода, простые способы получить все остальное. Все эти методы хранят файлы в общедоступных местах, так что будьте осторожны с ними.

внешние методы файлов и каталогов в контекст

| Method                                                                                |
| --------------------------------------------------------------------------------------|
|File getExternalCacheDir()                                                             |
| - Returns a handle to a cache folder in primary external storage. Treat it like you do|
|   getCacheDir(), except a little more carefully. Android is even less likely to clean |
|   up this folder than the private storage one.                                        |
|                                                                                       |
|File[] getExternalCacheDirs()                                                          |
| - Returns cache folders for multiple external storage locations.                      |
|                                                                                       |
|File getExternalFilesDir(String)                                                       |
| - Returns a handle to a folder on primary external storage in which to store regular  |
|   files. If you pass in a type String, you can access a specific subfolder dedicated  |
|   to a particular type of content. Type constants are defined in Environment, where   |
|   they are prefixed with DIRECTORY_.                                                  |
|   For example, pictures go in Environment.DIRECTORY_PICTURES.                         |
|                                                                                       |
|File[] getExternalFilesDirs(String)                                                    |
| - Same as getExternalFilesDir(String), but returns all possible file folders for the  |
|   given type.                                                                         |
|                                                                                       |
|File[] getExternalMediaDirs()                                                          |
| - Returns handles to all the external folders Android makes available for storing     |
|   media – pictures, movies, and music. What makes this different from calling         |
|   getExternalFilesDir(Environment.DIRECTORY_PICTURES) is that the media scanner       |
|   automatically scans this folder. The media scanner makes files available to         |
|   applications that play music, or browse movies and photos, so anything that you     |
|   put in a folder returned by getExternalMediaDirs() will automatically appear in     |
|   those apps.                                                                         |

технически внешние папки, указанные выше, могут быть недоступны, поскольку некоторые устройства используют съемную SD-карту для внешнего хранения. На практике это редко бывает проблемой, потому что почти все современные устройства имеют несъемное внутреннее хранилище для своего "внешнего" хранилища. Так что не стоит идти на крайние меры, чтобы объясните это. Но мы рекомендуем включить простой код для защиты от возможности, которую вы сделаете через минуту.

разрешение внешнего хранения

в общем, вам нужно разрешение на запись или чтение из внешнего хранилища. Разрешения-это хорошо известные строковые значения, которые вы помещаете в манифест с помощью <uses-permission> тег. Они говорят Android, что вы хотите сделать то, что Android хочет, чтобы вы попросили разрешения.

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

в Android 4.4, KitKat, они ослабили это ограничение. С Context.getExternalFilesDir(String) возвращает папку, специфичную для вашего приложения, имеет смысл, что вы хотели бы иметь возможность читать и писать файлы, которые там живут. Так что на Android 4.4 и выше (API 19) и выше, вам не нужно это разрешение для этой папки. (Но вам все еще нужно оно для других видов внешнего хранения.)

добавьте строку в манифест, которая запрашивает разрешение на чтение внешнего хранилища, но только до API листинг 16.5 запрос разрешения внешнего хранилища (AndroidManifest.xml)

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.bignerdranch.android.criminalintent" >
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
         android:maxSdkVersion="18" />

атрибут maxSdkVersion делает так, что ваше приложение запрашивает только это разрешение на версии Android, которые старше, чем API 19, Android KitKat. Обратите внимание, что вы просите только прочитать внешнее хранилище. Существует также WRITE_EXTERNAL_STORAGE разрешение, но вам это не нужно. Вы не будете ничего писать на внешнее хранилище: приложение камеры сделает это за вас

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

ссылки:

больше читать вещи:

отказ от ответственности: эта информация была взята из Android Programming: The Big Nerd Ranch Guide с разрешения авторов. Для получения дополнительной информации об этой книге или приобрести копию, пожалуйста, посетите bignerdranch.com.


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

в основном, он считывает доступные точки монтирования, отфильтровывает недопустимые, проверяет остальные, если они доступны, и добавляет их, если все условия выполнены.

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

// Notice: FileSystemDevice is just my own wrapper class. Feel free to replace it with your own. 

private List<FileSystemDevice> getDevices() {

    List<FileSystemDevice> devices = new ArrayList<>();

    // Add default external storage if available.
    File sdCardFromSystem = null;
    switch(Environment.getExternalStorageState()) {
        case Environment.MEDIA_MOUNTED:
        case Environment.MEDIA_MOUNTED_READ_ONLY:
        case Environment.MEDIA_SHARED:
            sdCardFromSystem = Environment.getExternalStorageDirectory();
            break;
    }

    if (sdCardFromSystem != null) {
        devices.add(new FileSystemDevice(sdCardFromSystem));
    }

    // Read /proc/mounts and add all mount points that are available
    // and are not "special". Also, check if the default external storage
    // is not contained inside the mount point. 
    try {
        FileInputStream fs = new FileInputStream("/proc/mounts");
        String mounts = IOUtils.toString(fs, "UTF-8");
        for(String line : mounts.split("\n")) {
            String[] parts = line.split(" ");

            // parts[0] - mount type
            // parts[1] - mount point
            if (parts.length > 1) {
                try {

                    // Skip "special" mount points and mount points that can be accessed
                    // directly by Android's functions. 
                    if (parts[0].equals("proc")) { continue; }
                    if (parts[0].equals("rootfs")) { continue; }
                    if (parts[0].equals("devpts")) { continue; }
                    if (parts[0].equals("none")) { continue; }
                    if (parts[0].equals("sysfs")) { continue; }
                    if (parts[0].equals("selinuxfs")) { continue; }
                    if (parts[0].equals("debugfs")) { continue; }
                    if (parts[0].equals("tmpfs")) { continue; }
                    if (parts[1].equals(Environment.getRootDirectory().getAbsolutePath())) { continue; }
                    if (parts[1].equals(Environment.getDataDirectory().getAbsolutePath())) { continue; }
                    if (parts[1].equals(Environment.getExternalStorageDirectory().getAbsolutePath())) { continue; }

                    // Verify that the mount point is accessible by listing its content. 
                    File file = new File(parts[1]);
                    if (file.listFiles() != null) {
                        try {

                            // Get canonical path for case it's just symlink to another mount point.
                            String devPath = file.getCanonicalPath();

                            for(FileSystemDevice device : devices) {

                                if (!devices.contains(devPath)) {                        
                                    devices.add(new FileSystemDevice(new File(devPath)));
                                }

                            }
                        } catch (Exception e) {
                            // Silently skip the exception as it can only occur if the mount point is not valid. 
                            e.printStackTrace();
                        }
                    }
                } catch (Exception e) {
                    // Silently skip the exception as it can only occur if the mount point is not valid. 
                    e.printStackTrace();
                }
            }
        }

        fs.close();
    } catch (FileNotFoundException e) {
        // Silently skip the exception as it can only occur if the /proc/mounts file is unavailable. 
        // Possibly, another detection method can be called here.
        e.printStackTrace();
    } catch (IOException e) {
        // Silently skip the exception as it can only occur if the /proc/mounts file is unavailable.
        // Possibly, another detection method can be called here.
        e.printStackTrace();            
    }

    return devices;
}

вот способ создания нового файла во внешнем хранилище (SDCard, если он присутствует в устройстве или внешнем хранилище устройства, если нет). Просто замените " foldername "на имя желаемой папки назначения и" filename " на имя файла, который вы сохраняете. Конечно, здесь вы можете увидеть, как сохранить общий файл, теперь вы можете искать, как сохранить изображения может здесь или что-то в файле.

try {
            File dir =  new File(Environment.getExternalStorageDirectory() + "/foldername/");
            if (!dir.exists()){
                dir.mkdirs();
            }
            File sdCardFile = new File(Environment.getExternalStorageDirectory() + "/foldername/" + fileName );
            int num = 1;
            String fileNameAux = fileName;
            while (sdCardFile.exists()){
                fileNameAux = fileName+"_"+num;
                sdCardFile = new File(Environment.getExternalStorageDirectory() + "/foldername/" + fileNameAux);
                num++;
            }

Это также определяет, что файл существует и добавляет число в конце имени файла, чтобы сохранить его.

надеюсь, что это помогает!

EDIT: Извините, я забыл, что вы должны попросить <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> в манифесте (или программно, если вы предпочитаете зефир)


 
`Log.d(TAG, System.getenv("SECONDARY_STORAGE"));`

выход:

`D/MainActivity: /storage/extSdCard`

работа над примечанием 3 Android 5.0.


для версий ниже Marshmallow вы можете напрямую предоставить разрешения в манифесте.

но для устройств с зефиром и выше вам нужно предоставить разрешения на время выполнения.

С помощью

Environment.getExternalStorageDirectory();

вы можете сразу получить доступ к внешней карте СД (установленной одной) Надеюсь, это поможет.