W wielu przypadkach aplikacja tworzy pliki, do których inne aplikacje nie muszą mieć dostępu lub nie powinny go mieć. System udostępnia te lokalizacje do przechowywania takich plików specyficznych dla aplikacji:
Katalogi pamięci wewnętrznej: te katalogi obejmują zarówno dedykowaną lokalizację do przechowywania trwałych plików, jak i inną lokalizację do przechowywania danych z pamięci podręcznej. System uniemożliwia innym aplikacjom dostęp do tych lokalizacji, a w Androidzie 10 (poziom interfejsu API 29) i nowszych wersjach są one szyfrowane. Dzięki tym cechom te lokalizacje są dobrym miejscem do przechowywania danych wrażliwych, do których dostęp ma tylko Twoja aplikacja.
Katalogi pamięci zewnętrznej: te katalogi obejmują zarówno dedykowaną lokalizację do przechowywania trwałych plików, jak i inną lokalizację do przechowywania danych z pamięci podręcznej. Chociaż inna aplikacja może uzyskać dostęp do tych katalogów jeśli ma odpowiednie uprawnienia, pliki przechowywane w tych katalogach są przeznaczone do użytku tylko przez Twoją aplikację. Jeśli chcesz utworzyć pliki do których inne aplikacje powinny mieć dostęp, Twoja aplikacja powinna przechowywać je w części pamięci zewnętrznej przeznaczonej na dane współdzielone zamiast tego.
Gdy użytkownik odinstaluje Twoją aplikację, pliki zapisane w pamięci specyficznej dla aplikacji zostaną usunięte. Z tego powodu nie należy używać tej pamięci do zapisywania niczego, co użytkownik chce zachować niezależnie od Twojej aplikacji. Jeśli na przykład Twoja aplikacja umożliwia użytkownikom robienie zdjęć, użytkownik będzie oczekiwać, że będzie mógł uzyskać dostęp do tych zdjęć nawet po odinstalowaniu aplikacji. Dlatego do zapisywania tego typu plików w odpowiedniej kolekcji multimediów należy używać pamięci współdzielonej.
W kolejnych sekcjach opisujemy, jak przechowywać pliki w katalogach specyficznych dla aplikacji i jak uzyskiwać do nich dostęp.
Dostęp z pamięci wewnętrznej
W przypadku każdej aplikacji system udostępnia katalogi w pamięci wewnętrznej, w których aplikacja może organizować swoje pliki. Jeden katalog jest przeznaczony na trwałe pliki aplikacji, a drugi zawiera pliki z pamięci podręcznej aplikacji. Aby odczytywać i zapisywać pliki w tych katalogach, aplikacja nie wymaga żadnych uprawnień systemowych.
Inne aplikacje nie mogą uzyskiwać dostępu do plików przechowywanych w pamięci wewnętrznej. Dzięki temu pamięć wewnętrzna jest dobrym miejscem na dane aplikacji, do których inne aplikacje nie powinny mieć dostępu.
Pamiętaj jednak, że te katalogi są zwykle małe. Zanim aplikacja zapisze pliki specyficzne dla aplikacji w pamięci wewnętrznej, powinna sprawdzić ilość wolnego miejsca na urządzeniu.
Dostęp do trwałych plików
Zwykłe, trwałe pliki aplikacji znajdują się w katalogu, do którego możesz
uzyskać dostęp za pomocą filesDir
właściwości obiektu kontekstu. Framework udostępnia kilka metod, które pomagają uzyskiwać dostęp do plików w tym katalogu i je przechowywać.
Dostęp do plików i ich przechowywanie
Do uzyskiwania dostępu do plików i ich przechowywania możesz używać interfejsu API File.
Aby zachować wydajność aplikacji, nie otwieraj i nie zamykaj tego samego pliku wielokrotnie.
Ten fragment kodu pokazuje, jak używać interfejsu API File:
Kotlin
val file = File(context.filesDir, filename)
Java
File file = new File(context.getFilesDir(), filename);
Przechowywanie pliku za pomocą strumienia
Zamiast używać interfejsu API File możesz wywołać
openFileOutput()
, aby uzyskać FileOutputStream, który zapisuje
dane w pliku w katalogu filesDir.
Ten fragment kodu pokazuje, jak zapisać tekst w pliku:
Kotlin
val filename = "myfile" val fileContents = "Hello world!" context.openFileOutput(filename, Context.MODE_PRIVATE).use { it.write(fileContents.toByteArray()) }
Java
String filename = "myfile"; String fileContents = "Hello world!"; try (FileOutputStream fos = context.openFileOutput(filename, Context.MODE_PRIVATE)) { fos.write(fileContents.toByteArray()); }
Aby umożliwić innym aplikacjom dostęp do plików przechowywanych w
tym katalogu w pamięci wewnętrznej, użyj
FileProvider z atrybutem
FLAG_GRANT_READ_URI_PERMISSION.
Dostęp do pliku za pomocą strumienia
Aby odczytać plik jako strumień, użyj
openFileInput():
Kotlin
context.openFileInput(filename).bufferedReader().useLines { lines -> lines.fold("") { some, text -> "$some\n$text" } }
Java
FileInputStream fis = context.openFileInput(filename); InputStreamReader inputStreamReader = new InputStreamReader(fis, StandardCharsets.UTF_8); StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader reader = new BufferedReader(inputStreamReader)) { String line = reader.readLine(); while (line != null) { stringBuilder.append(line).append('\n'); line = reader.readLine(); } } catch (IOException e) { // Error occurred when opening raw file for reading. } finally { String contents = stringBuilder.toString(); }
Wyświetlanie listy plików
Aby uzyskać tablicę zawierającą nazwy wszystkich plików w katalogu filesDir
wywołaj
fileList(), jak pokazano w
tym fragmencie kodu:
Kotlin
var files: Array<String> = context.fileList()
Java
Array<String> files = context.fileList();
Tworzenie zagnieżdżonych katalogów
Możesz też tworzyć zagnieżdżone katalogi lub otwierać katalog wewnętrzny, wywołując
getDir() w kodzie opartym na Kotlinie lub przekazując katalog główny i nazwę nowego katalogu do konstruktora File
w kodzie opartym na Javie:
Kotlin
context.getDir(dirName, Context.MODE_PRIVATE)
Java
File directory = context.getFilesDir(); File file = new File(directory, filename);
Tworzenie plików z pamięci podręcznej
Jeśli musisz przechowywać dane wrażliwe tylko tymczasowo, zapisz je w wyznaczonym katalogu pamięci podręcznej aplikacji w pamięci wewnętrznej. Podobnie jak w przypadku całej pamięci specyficznej dla aplikacji, pliki przechowywane w tym katalogu są usuwane, gdy użytkownik odinstaluje Twoją aplikację, chociaż pliki w tym katalogu mogą zostać usunięte wcześniej.
Aby utworzyć plik z pamięci podręcznej, wywołaj
File.createTempFile():
Kotlin
File.createTempFile(filename, null, context.cacheDir)
Java
File.createTempFile(filename, null, context.getCacheDir());
Aplikacja uzyskuje dostęp do pliku w tym katalogu za pomocą właściwości
cacheDir obiektu
kontekstu i interfejsu API File:
Kotlin
val cacheFile = File(context.cacheDir, filename)
Java
File cacheFile = new File(context.getCacheDir(), filename);
Usuwanie plików z pamięci podręcznej
Chociaż Android czasami sam usuwa pliki z pamięci podręcznej, nie należy polegać na tym, że system zwolni miejsce, usuwając te pliki za Ciebie. Zawsze należy utrzymywać pliki z pamięci podręcznej aplikacji w pamięci wewnętrznej.
Aby usunąć plik z katalogu pamięci podręcznej w pamięci wewnętrznej, użyj jednej z tych metod:
Metoda
delete()w obiekcieFilektóry reprezentuje plik:Kotlin
cacheFile.delete()
Java
cacheFile.delete();
Metoda
deleteFile()kontekstu aplikacji, do której przekazywana jest nazwa pliku:Kotlin
context.deleteFile(cacheFileName)
Java
context.deleteFile(cacheFileName);
Dostęp z pamięci zewnętrznej
Jeśli pamięć wewnętrzna nie zapewnia wystarczającej ilości miejsca na pliki specyficzne dla aplikacji, rozważ użycie pamięci zewnętrznej. System udostępnia katalogi w pamięci zewnętrznej, w których aplikacja może organizować pliki, które są przydatne dla użytkownika tylko w Twojej aplikacji. Jeden katalog jest przeznaczony na trwałe pliki aplikacji, a drugi zawiera pliki z pamięci podręcznej aplikacji.
W Androidzie 4.4 (poziom interfejsu API 19) lub nowszym aplikacja nie musi prosić o żadne uprawnienia związane z pamięcią, aby uzyskać dostęp do katalogów specyficznych dla aplikacji w pamięci zewnętrznej. Pliki przechowywane w tych katalogach są usuwane po odinstalowaniu aplikacji.
Na urządzeniach z Androidem 9 (poziom interfejsu API 28) lub starszym aplikacja może uzyskiwać dostęp do plików specyficznych dla aplikacji należących do innych aplikacji, pod warunkiem że ma odpowiednie uprawnienia do pamięci. Aby dać użytkownikom większą kontrolę nad plikami i ograniczyć ich bałagan, aplikacje kierowane na Androida 10 (poziom interfejsu API 29) i nowsze domyślnie mają ograniczony dostęp do pamięci zewnętrznej, czyli pamięci o ograniczonym zakresie. Gdy włączona jest pamięć ograniczona, aplikacje nie mogą uzyskiwać dostępu do katalogów specyficznych dla aplikacji należących do innych aplikacji.
Sprawdzanie, czy pamięć jest dostępna
Pamięć zewnętrzna znajduje się na woluminie fizycznym, który użytkownik może usunąć. Zanim spróbujesz odczytać dane specyficzne dla aplikacji z pamięci zewnętrznej lub zapisać w niej dane specyficzne dla aplikacji, sprawdź, czy wolumin jest dostępny.
Stan woluminu możesz sprawdzić, wywołując
Environment.getExternalStorageState().
Jeśli zwrócony stan to
MEDIA_MOUNTED, to
możesz odczytywać i zapisywać pliki specyficzne dla aplikacji w pamięci zewnętrznej. Jeśli jest to
MEDIA_MOUNTED_READ_ONLY,
możesz tylko odczytywać te pliki.
Na przykład te metody są przydatne do określania dostępności pamięci:
Kotlin
// Checks if a volume containing external storage is available // for read and write. fun isExternalStorageWritable(): Boolean { return Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED } // Checks if a volume containing external storage is available to at least read. fun isExternalStorageReadable(): Boolean { return Environment.getExternalStorageState() in setOf(Environment.MEDIA_MOUNTED, Environment.MEDIA_MOUNTED_READ_ONLY) }
Java
// Checks if a volume containing external storage is available // for read and write. private boolean isExternalStorageWritable() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } // Checks if a volume containing external storage is available to at least read. private boolean isExternalStorageReadable() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) || Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED_READ_ONLY); }
Na urządzeniach bez wymiennej pamięci zewnętrznej użyj tego polecenia, aby włączyć wolumin wirtualny do testowania logiki dostępności pamięci zewnętrznej:
adb shell sm set-virtual-disk true
Wybieranie fizycznej lokalizacji pamięci
Czasami urządzenie, które przydziela partycję pamięci wewnętrznej jako pamięć zewnętrzną, ma też gniazdo na kartę SD. Oznacza to, że urządzenie ma wiele woluminów fizycznych, które mogą zawierać pamięć zewnętrzną, więc musisz wybrać, którego z nich użyć do przechowywania danych specyficznych dla aplikacji.
Aby uzyskać dostęp do różnych lokalizacji, wywołaj
ContextCompat.getExternalFilesDirs().
Jak pokazano w tym fragmencie kodu, pierwszy element zwróconej tablicy jest uważany za podstawowy wolumin pamięci zewnętrznej. Używaj tego woluminu, chyba że jest pełny lub niedostępny.
Kotlin
val externalStorageVolumes: Array<out File> = ContextCompat.getExternalFilesDirs(applicationContext, null) val primaryExternalStorage = externalStorageVolumes[0]
Java
File[] externalStorageVolumes = ContextCompat.getExternalFilesDirs(getApplicationContext(), null); File primaryExternalStorage = externalStorageVolumes[0];
Dostęp do trwałych plików
Aby uzyskać dostęp do plików specyficznych dla aplikacji z pamięci zewnętrznej, wywołaj
getExternalFilesDir().
Aby zachować wydajność aplikacji, nie otwieraj i nie zamykaj tego samego pliku wielokrotnie.
Ten fragment kodu pokazuje, jak wywołać getExternalFilesDir():
Kotlin
val appSpecificExternalDir = File(context.getExternalFilesDir(null), filename)
Java
File appSpecificExternalDir = new File(context.getExternalFilesDir(null), filename);
Tworzenie plików z pamięci podręcznej
Aby dodać plik specyficzny dla aplikacji do pamięci podręcznej w pamięci zewnętrznej, uzyskaj odniesienie do
externalCacheDir:
Kotlin
val externalCacheFile = File(context.externalCacheDir, filename)
Java
File externalCacheFile = new File(context.getExternalCacheDir(), filename);
Usuwanie plików z pamięci podręcznej
Aby usunąć plik z zewnętrznego katalogu pamięci podręcznej, użyj metody
delete() w obiekcie File, który
reprezentuje plik:
Kotlin
externalCacheFile.delete()
Java
externalCacheFile.delete();
Treści multimedialne
Jeśli Twoja aplikacja działa z plikami multimedialnymi, które są przydatne dla użytkownika tylko w Twojej aplikacji, najlepiej przechowywać je w katalogach specyficznych dla aplikacji w pamięci zewnętrznej, jak pokazano w tym fragmencie kodu:
Kotlin
fun getAppSpecificAlbumStorageDir(context: Context, albumName: String): File? { // Get the pictures directory that's inside the app-specific directory on // external storage. val file = File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName) if (!file?.mkdirs()) { Log.e(LOG_TAG, "Directory not created") } return file }
Java
@Nullable File getAppSpecificAlbumStorageDir(Context context, String albumName) { // Get the pictures directory that's inside the app-specific directory on // external storage. File file = new File(context.getExternalFilesDir( Environment.DIRECTORY_PICTURES), albumName); if (file == null || !file.mkdirs()) { Log.e(LOG_TAG, "Directory not created"); } return file; }
Ważne jest, aby używać nazw katalogów podanych przez stałe interfejsu API, takie jak
DIRECTORY_PICTURES.
Te nazwy katalogów zapewniają, że system będzie prawidłowo traktować pliki.
Jeśli żadna z predefiniowanych nazw podkatalogów nie pasuje do Twoich plików, możesz
zamiast tego przekazać null do getExternalFilesDir(). Spowoduje to zwrócenie głównego katalogu specyficznego dla aplikacji w pamięci zewnętrznej.
Sprawdzanie ilości wolnego miejsca
Wielu użytkowników ma mało miejsca na urządzeniach, dlatego Twoja aplikacja powinna oszczędnie korzystać z miejsca.
Jeśli znasz ilość przechowywanych danych, możesz sprawdzić, ile
miejsca urządzenie może udostępnić Twojej aplikacji, wywołując
getAllocatableBytes().
Wartość zwracana przez getAllocatableBytes() może być większa niż bieżąca ilość wolnego miejsca na urządzeniu. Dzieje się tak, ponieważ system zidentyfikował pliki, które może usunąć z katalogów pamięci podręcznej innych aplikacji.
Jeśli jest wystarczająco dużo miejsca na zapisanie danych aplikacji, wywołaj
allocateBytes().
W przeciwnym razie aplikacja może poprosić użytkownika o usunięcie niektórych
plików z urządzenia lub usunięcie wszystkich plików z pamięci podręcznej
plików z urządzenia.
Ten fragment kodu pokazuje, jak aplikacja może sprawdzić ilość wolnego miejsca na urządzeniu:
Kotlin
// App needs 10 MB within internal storage. const val NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L; val storageManager = applicationContext.getSystemService<StorageManager>()!! val appSpecificInternalDirUuid: UUID = storageManager.getUuidForPath(filesDir) val availableBytes: Long = storageManager.getAllocatableBytes(appSpecificInternalDirUuid) if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) { storageManager.allocateBytes( appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP) } else { val storageIntent = Intent().apply { // To request that the user remove all app cache files instead, set // "action" to ACTION_CLEAR_APP_CACHE. action = ACTION_MANAGE_STORAGE } }
Java
// App needs 10 MB within internal storage. private static final long NUM_BYTES_NEEDED_FOR_MY_APP = 1024 * 1024 * 10L; StorageManager storageManager = getApplicationContext().getSystemService(StorageManager.class); UUID appSpecificInternalDirUuid = storageManager.getUuidForPath(getFilesDir()); long availableBytes = storageManager.getAllocatableBytes(appSpecificInternalDirUuid); if (availableBytes >= NUM_BYTES_NEEDED_FOR_MY_APP) { storageManager.allocateBytes( appSpecificInternalDirUuid, NUM_BYTES_NEEDED_FOR_MY_APP); } else { // To request that the user remove all app cache files instead, set // "action" to ACTION_CLEAR_APP_CACHE. Intent storageIntent = new Intent(); storageIntent.setAction(ACTION_MANAGE_STORAGE); }
Tworzenie aktywności zarządzania pamięcią
Aplikacja może zadeklarować i utworzyć niestandardową aktywność, która po uruchomieniu umożliwia użytkownikowi zarządzanie danymi przechowywanymi przez aplikację na urządzeniu użytkownika. Tę niestandardową aktywność "zarządzania miejscem" deklarujesz za pomocą
android:manageSpaceActivity
atrybutu w pliku manifestu. Aplikacje menedżera plików mogą wywoływać tę
aktywność
nawet wtedy, gdy Twoja aplikacja nie eksportuje aktywności, czyli gdy aktywność ma ustawioną wartość
android:exported na
false.
Prośba o usunięcie niektórych plików z urządzenia
Aby poprosić użytkownika o wybranie plików na urządzeniu do usunięcia, wywołaj intencję
która zawiera
ACTION_MANAGE_STORAGE
działanie. Ta intencja wyświetla użytkownikowi prośbę. W razie potrzeby ta prośba może pokazywać ilość wolnego miejsca na urządzeniu. Aby wyświetlić te przyjazne dla użytkownika informacje, użyj wyniku tego obliczenia:
StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()
Prośba o usunięcie wszystkich plików z pamięci podręcznej
Możesz też poprosić użytkownika o wyczyszczenie plików z pamięci podręcznej wszystkich aplikacji na urządzeniu. Aby to zrobić, wywołaj intencję, która zawiera
ACTION_CLEAR_APP_CACHE
działanie intencji.
Dodatkowe materiały
Więcej informacji o zapisywaniu plików w pamięci urządzenia znajdziesz w tych materiałach.