Uniwersalny sposób zapisu na zewnętrzną kartę SD na Androidzie

W mojej aplikacji, muszę przechowywać wiele obrazów w pamięci urządzenia. Takie pliki mają tendencję do przechowywania urządzenia i chcę, aby użytkownicy mogli wybrać zewnętrzną kartę SD jako folder docelowy.

Czytam wszędzie, że Android nie pozwala użytkownikom na zapis na zewnętrznej karcie SD, przez kartę SD mam na myśli zewnętrzną i montowaną kartę SD i nie zewnętrzną pamięć , ale aplikacje menedżera plików zarządzają zapisem na zewnętrznej karcie SD na wszystkich wersjach Androida.

Jaki jest lepszy sposób na przyznanie dostępu do odczytu/zapisu zewnętrznej karcie SD na różnych poziomach API (Pre-KitKat, KitKat, Lollipop+)?

Update 1

Wypróbowałem metodę 1 z odpowiedzi Doomknight, bez skutku: Jak widzisz sprawdzam uprawnienia w środowisku wykonawczym przed próbą zapisu na 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();
    }
}

Ale dostaję błąd dostępu, próbowałem na dwóch różnych urządzeniach: HTC10 i 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.access$900(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
Author: albodelu, 2016-10-16

8 answers

Krótka odpowiedź.

ContextCompat.getExternalFilesDirs rozwiązuje Błąd dostępu , gdy nie trzeba udostępniać plików.

W tym celu należy skorzystać z narzędzia do przechowywania danych, które umożliwia dostęp do pamięci masowej.

Podsumowanie.

Nie ma uniwersalnego sposobu zapisu na zewnętrzną kartę SD na Androidzie ze względu na ciągły zmiany:

  • Pre-KitKat: oficjalna platforma Android nie obsługuje kart SD w ogóle z wyjątkiem WYJĄTKÓW.

  • KitKat: wprowadzono interfejsy API, które umożliwiają aplikacjom dostęp do plików w katalogach specyficznych dla aplikacji na kartach SD.

  • Lollipop: dodano interfejsy API, aby aplikacje mogły żądać dostępu do folderów należących do innych dostawców.

  • Nugat: udostępniono uproszczony interfejs API umożliwiający dostęp do wspólnej pamięci zewnętrznej katalogi.

Na podstawie Doomsknight ' s answer i mine oraz Dave Smith i Mark Murphy blog posty: 1, 2, 3:


1. uprawnienia.

Możesz przyznać dostęp do odczytu/zapisu zewnętrznej karcie SD na różnych poziomach api (API23 + at run time ).

W przypadku korzystania z katalogów specyficznych dla aplikacji, w przypadku korzystania z katalogów specyficznych dla aplikacji wymagane są następujące uprawnienia:]}

Z Kitkat Twoje szanse na "kompletne rozwiązanie" bez zakorzenienia są prawie zero: projekt Androida zdecydowanie spieprzył proszę. Żadne aplikacje nie uzyskują pełnego dostępu do zewnętrznej karty SD:

  • menedżery plików: nie można ich używać do zarządzania zewnętrzną kartą SD. W w większości obszarów mogą tylko czytać, ale nie pisać.
  • aplikacje multimedialne: nie możesz retag/ponownie zorganizować swoją kolekcję multimediów dłużej, jak te aplikacje nie mogę do niego napisać.
  • aplikacje biurowe: prawie to samo

The only place 3 rd party apps are allowed to napisz na swoim kartami zewnętrznymi są "własne katalogi" (tj. /sdcard/Android/data/<package_name_of_the_app>).

The only ways to naprawdę naprawić, które wymagają albo producenta (niektóre z nich stałe it, np. Huawei z ich aktualizacją Kitkat dla P6) - lub root... (Wyjaśnienie Izzy kontynuuje tutaj)


2. o uniwersalnym rozwiązaniu.

Historia mówi, że nie ma uniwersalnego sposobu zapisu na zewnętrzną kartę SD / Align = "left" / ..

Ten fakt jest demostratowany przez te przykłady konfiguracji pamięci zewnętrznej dla urządzeń.

Dostęp do pamięci zewnętrznej jest chroniony przez różne systemy Android uprawnienia. Począwszy od Androida 1.0, dostęp do zapisu jest chroniony za pomocą na WRITE_EXTERNAL_STORAGE pozwolenie . Począwszy od Androida 4.1, przeczytaj dostęp jest chroniony uprawnieniem READ_EXTERNAL_STORAGE.

Począwszy od Androida 4.4, właściciel, Grupa I tryby plików na zewnętrzne urządzenia pamięci masowej są teraz syntetyzowane w oparciu o Katalog struktura. Dzięki temu aplikacje mogą zarządzać swoimi pakietami katalogów na zewnętrznej pamięci masowej bez konieczności posiadania szerokiego Pozwolenie. Na przykład aplikacja z pakietem Nazwa com.example.foo może teraz swobodnie Android/data/com.example.foo/ na zewnętrznych urządzeniach pamięci masowej bez uprawnienia. Te zsyntetyzowane uprawnienia są realizowane przez pakowanie surowych urządzeń pamięci masowej w demona FUSE.

Android 6.0 wprowadza nowy model runtime permissions, w którym aplikacje żądaj możliwości w razie potrzeby w czasie wykonywania. Ponieważ nowy model zawiera READ/WRITE_EXTERNAL_STORAGE uprawnienia, Platforma musi dynamicznie przyznawać dostęp do pamięci masowej bez zabijania lub ponowne uruchamianie już uruchomionych aplikacji. Czyni to utrzymując trzy różne widoki wszystkich zamontowanych urządzeń pamięci masowej:

  • / mnt/runtime / default jest wyświetlany dla aplikacji bez specjalnej pamięci masowej uprawnienia...
  • / mnt / runtime / read jest wyświetlany w aplikacjach z READ_EXTERNAL_STORAGE
  • / mnt / runtime / write jest wyświetlany w aplikacjach z WRITE_EXTERNAL_STORAGE

3. o aktualizacji 1.

Użyłbym katalogów specyficznych dla aplikacji , aby uniknąć problemu z zaktualizowanym pytaniem i ContextCompat.getExternalFilesDirs() używając dokumentacji getExternalFilesDir jako odniesienia.

Poprawić heurystykę aby określić, co reprezentuje nośniki wymienne oparte na różnych poziomach api, takich jak android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT

Pamiętaj, że Android 6.0 obsługuje przenośne urządzenia pamięci masowej , a aplikacje innych firm muszą przejść przez platformę dostępu do pamięci masowej (Storage Access Framework) . Twoje urządzenia HTC10 i Shield K1 to prawdopodobnie API 23.

Twój dziennik pokazuje wyjątek odmowy pozwolenia dostęp do /mnt/media_rw, Jak ta poprawka dla 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>

Nigdy nie próbowałem, więc nie mogę udostępnić kodu ale uniknąłbym for próbowania zapisu na wszystkich zwracanych katalogach i szukania najlepszego dostępnego katalogu do zapisu na podstawie pozostałej przestrzeni .

Być może alternatywa Gizm0 do Twojej metody getStorageDirectories() to dobry punkt wyjścia.

ContextCompat.getExternalFilesDirs rozwiązuje problem, jeśli nie potrzebujesz dostępu do innych folderów.


4. Aby uzyskać uprawnienia w manifeście (Api w czasie wykonywania (Api >= 23).

Dodaj następny kod do AndroidManifest.xml i przeczytaj uzyskanie dostępu do zewnętrznej pamięci masowej

W celu ... zapisywanie plików w pamięci zewnętrznej, aplikacja musi / align = "left" / .. system uprawnienia:

<manifest ...>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest> 
Jeśli potrzebujesz obu..., musisz poprosić tylko pozwolenie WRITE_EXTERNAL_STORAGE.

Zignoruj następną notatkę z powodu błędów, ale spróbuj użyć ContextCompat.getExternalFilesDirs():

Uwaga: zaczynając od Android 4.4, te uprawnienia nie są wymagane jeśli czytasz lub piszesz tylko pliki, które są prywatne dla Twojej aplikacji. Więcej informacji... zobacz zapisywanie plików, które są APP-private .

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

Aby uzyskać uprawnienia w trybie runtime, jeśli API poziom 23 + i odczytać żądanie uprawnień w czasie wykonywania

Począwszy od Androida 6.0 (poziom API 23), użytkownicy przyznają uprawnienia do aplikacje, gdy aplikacja jest uruchomiona, a nie podczas instalacji aplikacji ... lub zaktualizuj aplikację ... użytkownik może odwołać uprawnienia.

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

5. Przed KitKat spróbuj użyć Doomsknight metoda 1, Metoda 2 inaczej.

Przeczytaj Wyjaśnienie Marka Murphy ' ego i zalecane Dianne Hackborn IDave Smith ones

    [[62]} do Androida 4.4, nie było oficjalnego wsparcia dla nośników wymiennych w Androidzie, począwszy od KitKat, koncepcja "podstawowa "i" wtórna " pamięć zewnętrzna pojawia się w interfejsie API FMW. [62]}wcześniejsze aplikacje polegają tylko na indeksowaniu MediaStore, wysyłaniu ze sprzętem lub sprawdzaniu punktów montowania i stosowaniu heurystyki, aby określić, co reprezentuje nośniki wymienne. [62]} od Androida 4.2 firma Google poprosiła producentów urządzeń o zablokowanie nośników wymiennych dla bezpieczeństwa (Obsługa wielu użytkowników), a nowe testy zostały dodane w 4.4.
  • ponieważ KiKat getExternalFilesDirs() i inne metody były dodano, aby zwrócić użyteczną ścieżkę dla wszystkich dostępnych woluminów pamięci (pierwszy zwracany element jest woluminem podstawowym).
  • poniższa tabela wskazuje, co programista może spróbować zrobić i jak KitKat zareaguje: Tutaj wpisz opis obrazka

Przed KitKat spróbuj użyć Doomsknight metoda 1 lub przeczytać ta odpowiedź przez Gnathonic lub gist:

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;
}

Przeczytaj również Wyjaśnienie Paolo Rovelli i spróbuj aby użyć rozwiązania Jeffa Sharkeya od KitKat:

W KitKat jest teraz publiczne API do interakcji z te dodatkowe współdzielone urządzenia pamięci masowej.

Nowy Context.getExternalFilesDirs() i Context.getExternalCacheDirs() metody mogą zwracać wiele ścieżek, w tym zarówno urządzenia pierwotne, jak i wtórne.

Można następnie iterację nad nimi i sprawdzić Environment.getStorageState() i File.getFreeSpace() aby określić najlepsze miejsce do przechowywania plików.

Metody te są również dostępne na ContextCompat w bibliotece support-v4.


6. Lollipop wprowadził również zmiany i klasę helper DocumentFile .

getStorageState Dodano w API 19, przestarzałe w API 21, użycie getExternalStorageState(File)

Oto świetny samouczek do interakcji z dostępem do pamięci masowej Framework w KitKat.

[[36]}interakcja z nowymi API w Lollipop jest bardzo podobna (Jeff Sharkey ' s Wyjaśnienie) .

7. Android 7.0 zapewnia uproszczony interfejs API do dostępu do zewnętrznych rejestrów pamięci masowej.

Scoped Directory Access W systemie Android 7.0 aplikacje mogą korzystać z nowych interfejsów API, aby zażądać dostępu do określonych pamięć zewnętrzna katalogi, w tym katalogi na nośnikach wymiennych takich jak karty SD...

Aby uzyskać więcej informacji, zobacz szkolenie z dostępu do katalogów Scoped .


8. Android O zmiany.

Uruchamianie w Androidzie O , the Storage Access Framework umożliwia niestandardowe dokumenty dostawcy aby utworzyć wyszukiwalne deskryptory plików dla plików znajdujących się w zdalnym źródło danych...

Uprawnienia , Przed uruchomieniem systemu Android o, Jeśli aplikacja poprosiła o pozwolenie w czasie wykonywania, a pozwolenie zostało przyznane, system również przyznał się nieprawidłowo aplikacji reszta uprawnień, które należały do tego samego / align = "center" bgcolor = "# e0ffe0 " / cesarz chin / / align = center /

W przypadku aplikacji kierujących na Androida o, to zachowanie zostało poprawione. Aplikacja otrzymuje tylko uprawnienia, o które wyraźnie prosiła. Jednak po udzieleniu przez użytkownika zezwolenia aplikacji, wszystkie kolejne żądania uprawnień w tej grupie uprawnień są automatycznie zgoda.

Na przykład, READ_EXTERNAL_STORAGE i WRITE_EXTERNAL_STORAGE...


9. Powiązane pytania i Polecane odpowiedzi.

Jak Mogę uzyskać zewnętrzną ścieżkę karty SD dla Androida 4.0+?

Mkdir () działa wewnątrz wewnętrznej pamięci flash, ale nie na karcie SD?

Różnica między getExternalFilesDir i getExternalStorageDirectory()

Dlaczego getExternalFilesDirs () nie działa na niektórych urządzeniach?

Jak korzystać z nowej karty SD access API przedstawione dla Androida 5.0 (Lollipop)

Pisanie na zewnątrz Karta SD w systemie Android 5.0 i nowszym

Uprawnienia zapisu na karcie SD systemu Android przy użyciu SAF (Storage Access Framework)

SAFFAQ: Storage Access Framework FAQ


10. powiązane błędy i problemy.

Błąd: na Androidzie 6, podczas korzystania z getExternalFilesDirs, nie pozwoli Ci tworzyć nowych plików w swoich wynikach

Zapis do katalogu zwracanego przez getExternalCacheDir () na Lollipop nie powiódł się bez zapisu pozwolenie

 84
Author: albodelu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-01-09 20:19:48

Wierzę, że istnieją dwie metody, aby to osiągnąć:

Metoda 1: (czy Nie działa na 6.0 i nowszych, ze względu na zmiany uprawnień)

Używam tej metody od lat na wielu wersjach urządzeń bez żadnego problemu. uznanie należy się oryginalnemu źródłu, ponieważ to nie ja go napisałem.

Zwróci wszystkie zamontowane nośniki (w tym prawdziwe karty SD) w liście lokalizacji katalogów ciągów. Z listy można następnie zapytać użytkownika, Gdzie Zapisz, itp.

Można go nazwać następującym:

 HashSet<String> extDirs = getStorageDirectories();

Metoda:

/**
 * 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;
}

Metoda 2:

Użyj biblioteki wsparcia v4

import android.support.v4.content.ContextCompat;
Po prostu zadzwoń pod poniższy adres, aby uzyskać listę miejsc przechowywania.
 File[] list = ContextCompat.getExternalFilesDirs(myContext, null);

Lokalizacje różnią się jednak w użyciu.

Zwraca bezwzględne ścieżki do katalogów specyficznych dla aplikacji na wszystkich zewnętrzne urządzenia pamięci masowej, w których aplikacja może umieszczać pliki trwałe jest właścicielem. Te pliki są wewnętrzne do aplikacji, a nie zazwyczaj widoczne dla użytkownika jako nośnik.

Zewnętrzne urządzenia pamięci masowej zwracane tutaj są uważane za stałą część urządzenie, w tym zarówno emulowane Pamięci zewnętrzne, jak i nośniki fizyczne gniazda, takie jak karty SD w komorze baterii. Zwrócone ścieżki do nie obejmuje urządzeń przejściowych, takich jak pamięci flash USB.

Aplikacja może przechowywać dane na dowolnym lub wszystkich zwracanych urządzeniach. Na przykład, aplikacja może wybrać przechowywanie dużych plików na urządzeniu z większość dostępnej przestrzeni

Więcej informacji na temat ContextCompat

Są jak pliki specyficzne dla aplikacji. Ukryte przed innymi aplikacjami.

 9
Author: Doomsknight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-10 15:57:52

Kolejna odpowiedź. Ta odpowiedź pokazuje tylko 5.0+, ponieważ uważam, że zamieszczona tutaj odpowiedź Doomknight jest najlepszym sposobem na Androida 4.4 i poniżej.

To jest pierwotnie opublikowany tutaj (czy istnieje sposób, aby uzyskać rozmiar karty SD w Androidzie?) przez mnie, aby uzyskać rozmiar zewnętrznej karty SD na Androida 5.0 +

Aby uzyskać zewnętrzną kartę SD jako 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;
}

Uwaga: dostaję tylko" pierwszą " zewnętrzną kartę sd, jednak można ją zmodyfikować i zwrócić ArrayList<File> zamiast File i pozwolić pętla kontynuuje zamiast wywoływać break Po znalezieniu pierwszej.

 2
Author: ᴛʜᴇᴘᴀᴛᴇʟ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:47

Oprócz wszystkich innych miłych odpowiedzi, mógłbym dodać trochę więcej do tego pytania, aby mogło dać szerszy zasięg dla czytelników. W mojej odpowiedzi tutaj, chciałbym użyć 2 policzalne zasoby do prezentacji zewnętrznej pamięci masowej.

[13]}pierwszy zasób pochodzi z Android Programming, The Big Nerd Ranch Guide 2nd edition, Rozdział 16, strona 294.

Książka opisuje podstawowe i zewnętrzne metody plików i katalogów. Postaram się zrobić CV z tego, co może być istotne dla Twojego pytanie.

Następująca część książki:

Pamięć Zewnętrzna

Twoje zdjęcie potrzebuje więcej niż miejsca na ekranie. Pełnowymiarowe zdjęcia są zbyt duże, aby trzymać się wewnątrz Bazy danych SQLite, a tym bardziej Intent. Będą potrzebować miejsca do zamieszkania w systemie plików Twojego urządzenia. Zwykle umieszczasz je w prywatnym magazynie. Przypomnij sobie, że korzystałeś z prywatnego magazynu aby zapisać bazę danych SQLite. Metodami takimi jak Context.getFileStreamPath(String) i Context.getFilesDir(), możesz zrobić to samo ze zwykłymi plikami (które będą żyły w podfolder sąsiadujący z podfolderem bazy danych, w którym mieszka twoja baza danych SQLite)

Podstawowe metody plików i katalogów w kontekście

| 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|

Jeśli przechowujesz pliki, z których musi korzystać tylko twoja obecna aplikacja, te metody są dokładnie tym, co potrzebujesz.

Z drugiej strony, jeśli potrzebujesz innej aplikacji do zapisu do tych plików, masz pecha: podczas gdy jest Context.MODE_WORLD_READABLE flaga, którą możesz przekazać openFileOutput(String, int), to na przestarzały i nie do końca niezawodny w działaniu na nowszych urządzeniach. Jeśli przechowujesz pliki do udostępnienia z innymi aplikacjami lub odbierającymi pliki z innych aplikacji( pliki takie jak zapisane zdjęcia), musisz je przechowywać w zamiast tego pamięć zewnętrzna.

Istnieją dwa rodzaje pamięci zewnętrznej: podstawowa i wszystko inne. Wszystkie urządzenia z Androidem mają w co najmniej jedna lokalizacja dla pamięci zewnętrznej: lokalizacja podstawowa, która znajduje się w folderze zwracanym przez Environment.getExternalStorageDirectory(). Może to być karta SD, ale obecnie jest to bardziej powszechnie zintegrowane z samym urządzeniem. Niektóre urządzenia mogą mieć dodatkową pamięć zewnętrzną. Że spadnie pod "Wszystko inne."

Context zapewnia również kilka metod uzyskiwania z pamięci zewnętrznej. Metody te zapewniają łatwe sposoby, aby dostać się do podstawowej pamięci zewnętrznej, i tak jakby-łatwe sposoby, aby dostać się do wszystkiego innego. Wszystkie metody te przechowują pliki również w publicznie dostępnych miejscach, więc uważaj na nie.

Plik zewnętrzny i metody katalogowe w kontekście

| 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.                                                                         |

Technicznie rzecz biorąc, zewnętrzne foldery podane powyżej mogą nie być dostępne, ponieważ niektóre urządzenia używają wymiennej karty SD do przechowywania zewnętrznego. W praktyce rzadko jest to problemem, ponieważ prawie wszystkie nowoczesne urządzenia mają nieusuwalną pamięć wewnętrzną na "zewnętrzną" pamięć masową. Nie warto więc posuwać się do skrajnych kroków, aby to wyjaśnić. Ale nie zaleca się w tym prosty kod, aby chronić przed możliwością, które można zrobić w chwila.

Pozwolenie na przechowywanie zewnętrzne

Ogólnie rzecz biorąc, potrzebujesz uprawnień do zapisu lub odczytu z pamięci zewnętrznej. Uprawnienia są dobrze znanymi wartościami łańcuchowymi, które umieszczasz w manifeście za pomocą znacznika <uses-permission>. Mówią Androidowi, że chcesz zrobić coś, o co Android chce poprosić o pozwolenie.

Tutaj Android oczekuje, że poprosisz o pozwolenie, ponieważ chce wyegzekwować pewną odpowiedzialność. Mówisz Androidowi, że musisz uzyskać dostęp do zewnętrznej pamięci masowej, a Android powie użytkownikowi, że jest to jedna z rzeczy, które robi Twoja aplikacja, gdy próbuje ją zainstalować. W ten sposób nikt nie jest zaskoczony, gdy zaczniesz zapisywać rzeczy na karcie SD.

W Androidzie 4.4, KitKat, poluzowali to ograniczenie. Ponieważ Context.getExternalFilesDir(String) zwraca folder, który jest specyficzny dla Twojej aplikacji, ma to sens, że chcesz mieć możliwość odczytu i zapisu plików, które tam mieszkają. Tak więc na Androidzie 4.4 (API 19) i nowszych, nie potrzebujesz tego uprawnienia do tego folderu. (Ale nadal potrzebujesz go do innych rodzajów pamięci zewnętrznej.)

Dodaj wiersz do manifestu, który żąda uprawnień do odczytu pamięci zewnętrznej, ale tylko do listy API 16.5 żądającej uprawnień pamięci zewnętrznej (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" />

Atrybut maxSdkVersion sprawia, że aplikacja prosi o to pozwolenie tylko w wersjach Androida, które są starsze niż API 19, Android KitKat. Zwróć uwagę, że prosisz tylko o odczyt pamięci zewnętrznej. Istnieje również WRITE_EXTERNAL_STORAGE pozwolenie, ale nie potrzebujesz tego. Nie będziesz zapisywać niczego do pamięci zewnętrznej: aplikacja aparatu zrobi to za ciebie

Drugim zasobem jest link przeczytaj wszystko, ale możesz również przejść do Za pomocą sekcji External Storage.

Numer referencyjny:

Czytaj więcej rzeczy:

Zastrzeżenie: Ta informacja została zaczerpnięta z Android Programming: The Big Nerd Ranch Guide za zgodą autorów. Aby uzyskać więcej informacji na temat tej książki lub kupić kopię, odwiedź bignerdranch.com.

 2
Author: maytham-ɯɐɥʇʎɐɯ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-09 10:00:02

Ten temat jest trochę stary, ale szukałem rozwiązania i po kilku badaniach przyszedłem z poniższym kodem, aby pobrać listę dostępnych "zewnętrznych" punktów montowania, które, zgodnie z moją wiedzą, działają na wielu różnych urządzeniach.

Zasadniczo odczytuje dostępne punkty montowania, odfiltrowuje nieprawidłowe, testuje Pozostałe, jeśli są dostępne i dodaje je, jeśli wszystkie warunki są spełnione.

Oczywiście wymagane uprawnienia muszą być przyznane zanim kod zostanie / align = "left" /

// 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;
}
 0
Author: Václav Hodek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-30 23:20:08

Oto sposób tworzenia nowego pliku w pamięci zewnętrznej (SDCard, jeśli jest obecny w urządzeniu lub pamięci zewnętrznej urządzenia, jeśli nie). Wystarczy zastąpić "foldername "nazwą wybranego folderu docelowego i" filename " nazwą pliku, który zapisujesz. Oczywiście tutaj możesz zobaczyć, jak zapisać plik generyczny, teraz możesz wyszukać, jak zapisać obrazy może tutaj lub cokolwiek w pliku.

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++;
            }

To również kontroluje, że plik istnieje i dodaje liczbę na końcu nazwa nowego pliku do zapisania.

Mam nadzieję, że to pomoże!

EDIT: Przepraszam, zapomniałem, że musisz poprosić o <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> w manifeście (lub programowo, jeśli wolisz od Marshmallow)

 -1
Author: Hugo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:42
 
`Log.d(TAG, System.getenv("SECONDARY_STORAGE"));`

Wyjście:

`D/MainActivity: /storage/extSdCard`

Pracuje nad Note 3 Android 5.0.

 -2
Author: user2773829,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-26 05:10:15

Dla wersji poniżej Marshmallow możesz bezpośrednio nadać uprawnienia w manifeście.

Ale dla urządzeń z Marshmallow i powyżej musisz przyznać uprawnienia na czas uruchomienia.

Za pomocą

Environment.getExternalStorageDirectory();

Można bezpośrednio uzyskać dostęp do zewnętrznej karty SD (zamontowanej) Mam nadzieję, że to pomoże.

 -3
Author: Geet Choubey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-10-16 10:08:13