W wielu przypadkach Twoja aplikacja tworzy pliki, do których inne aplikacje nie powinny mieć dostępu. System udostępnia te lokalizacje do przechowywania takich plików aplikacji:
Katalogi pamięci wewnętrznej: te katalogi zawierają 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 na Androidzie 10 (poziom interfejsu API 29) i nowszych lokalizacje są zaszyfrowane. Te cechy sprawiają, że te lokalizacje są dobrym miejscem do przechowywania danych poufnych, do których dostęp ma tylko Twoja aplikacja.
Katalogi zewnętrznego miejsca na dane: te katalogi zawierają zarówno dedykowane miejsce na trwałe pliki, jak i inne miejsce na dane 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 tylko do użytku Twojej aplikacji. Jeśli chcesz tworzyć pliki, do których inne aplikacje będą miały dostęp, przechowuj je w sekcji pamięci współdzielonej w pamięci zewnętrznej.
Gdy użytkownik odinstaluje Twoją aplikację, pliki zapisane w pamięci aplikacji zostaną usunięte. Z tego powodu nie należy używać tego miejsca do przechowywania do zapisywania czegokolwiek, co użytkownik chce zachować niezależnie od aplikacji. Jeśli na przykład Twoja aplikacja umożliwia użytkownikom robienie zdjęć, użytkownik powinien mieć do nich dostęp nawet po odinstalowaniu aplikacji. Dlatego do zapisywania takich plików w odpowiedniej kolekcji multimediów lepiej jest używać współdzielonego miejsca na dane.
W sekcjach poniżej opisujemy, jak przechowywać pliki i uzyskać do nich dostęp w katalogach konkretnych aplikacji.
Dostęp z pamięci wewnętrznej
Dla każdej aplikacji system udostępnia w pamięci wewnętrznej katalogi, w których aplikacja może porządkować pliki. Jeden katalog jest przeznaczony do trwałych plików aplikacji, a drugi do plików z pamięci podręcznej aplikacji. Aplikacja nie wymaga żadnych uprawnień systemowych do odczytu i zapisu plików w tych katalogach.
Inne aplikacje nie mają dostępu do plików przechowywanych w pamięci wewnętrznej. Dzięki temu pamięć wewnętrzna to dobre miejsce do przechowywania danych aplikacji, do których inne aplikacje nie powinny mieć dostępu.
Pamiętaj jednak, że te katalogi są zwykle niewielkie. Przed zapisaniem plików aplikacji w pamięci wewnętrznej aplikacja powinna wysłać zapytanie o wolne miejsce na urządzeniu.
Dostęp do plików stałych
Zwykłe trwałe pliki aplikacji znajdują się w katalogu, do którego możesz uzyskać dostęp za pomocą właściwości filesDir
obiektu kontekstu. Framework udostępnia kilka metod umożliwiających dostęp do plików w tym katalogu i ich przechowywanie.
Dostęp do plików i ich przechowywanie
Aby uzyskać dostęp do plików i je przechowywać, możesz użyć interfejsu API File
.
Aby zachować wydajność aplikacji, nie otwieraj i zamykaj tego samego pliku kilka razy.
Ten fragment kodu pokazuje, jak korzystać z interfejsu API File
:
Kotlin
val file = File(context.filesDir, filename)
Java
File file = new File(context.getFilesDir(), filename);
Przechowywanie pliku za pomocą strumienia
Zamiast korzystać z interfejsu API File
możesz wywołać metodę 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 zezwolić innym aplikacjom na dostęp do plików przechowywanych w tym katalogu w pamięci wewnętrznej, użyj atrybutu FileProvider
z wartością FLAG_GRANT_READ_URI_PERMISSION
.
Uzyskiwanie dostępu do pliku przy użyciu 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
Tablicę zawierającą nazwy wszystkich plików w katalogu filesDir
można uzyskać, wywołując metodę fileList()
, jak pokazano w tym fragmencie kodu:
Kotlin
var files: Array<String> = context.fileList()
Java
Array<String> files = context.fileList();
Tworzenie katalogów zagnieżdżonych
Możesz też utworzyć zagnieżdżone katalogi lub otworzyć katalog wewnętrzny, wywołując funkcję getDir()
w kodzie Kotlina lub przekazując katalog główny i nazwę nowego katalogu do konstruktora File
w kodzie Java:
Kotlin
context.getDir(dirName, Context.MODE_PRIVATE)
Java
File directory = context.getFilesDir(); File file = new File(directory, filename);
Tworzenie plików pamięci podręcznej
Jeśli dane wrażliwe mają być przechowywane tylko tymczasowo, zapisz je w katalogu pamięci podręcznej aplikacji w pamięci wewnętrznej. Podobnie jak w przypadku wszystkich pamięci specyficznych dla aplikacji, pliki przechowywane w tym katalogu są usuwane, gdy użytkownik odinstaluje aplikację, ale mogą zostać usunięte wcześniej.
Aby utworzyć plik w pamięci podręcznej, wywołaj funkcję 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 pamięci podręcznej
Chociaż Android czasami samodzielnie usuwa pliki pamięci podręcznej, nie należy polegać na tym, że system sam je usunie. Pliki pamięci podręcznej aplikacji powinny zawsze znajdować się 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 obiekcieFile
, który reprezentuje plik:Kotlin
cacheFile.delete()
Java
cacheFile.delete();
Metoda
deleteFile()
kontekstu aplikacji, do której przekazujesz nazwę pliku:Kotlin
context.deleteFile(cacheFileName)
Java
context.deleteFile(cacheFileName);
Dostęp z pamięci zewnętrznej
Jeśli w pamięci wewnętrznej jest za mało miejsca na pliki związane z aplikacją, rozważ użycie pamięci zewnętrznej. System udostępnia katalogi w pamięci zewnętrznej, w których aplikacja może porządkować pliki, które są przydatne dla użytkownika tylko w ramach aplikacji. Jeden katalog jest przeznaczony dla trwałych plików aplikacji, a drugi zawiera pliki z buforu 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 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 Twoja aplikacja może uzyskać dostęp do plików należących do innych aplikacji, o ile ma odpowiednie uprawnienia do przechowywania. Aby zapewnić użytkownikom większą kontrolę nad plikami i ograniczyć ich nagromadzenie, aplikacje kierowane na Androida 10 (poziom interfejsu API 29) lub nowszego mają domyślnie ograniczony dostęp do pamięci zewnętrznej, czyli ograniczony dostęp do miejsca na dane. Gdy ograniczone miejsce na dane jest włączone, aplikacje nie mają dostępu do katalogów aplikacji, które należą do innych aplikacji.
Sprawdź, czy jest dostępne miejsce na dane
Ponieważ pamięć zewnętrzna znajduje się na fizycznym woluminie, który użytkownik może usunąć, przed odczytaniem danych aplikacji z pamieci zewnętrznej lub zapisaniem danych aplikacji do niej sprawdź, czy wolumin jest dostępny.
Stan głośności możesz sprawdzić, wywołując funkcję Environment.getExternalStorageState()
.
Jeśli zwrócony stan to
MEDIA_MOUNTED
,
możesz odczytywać i zapisywać pliki związane z aplikacją na zewnętrznym urządzeniu pamięci masowej. Jeśli jest to MEDIA_MOUNTED_READ_ONLY
, możesz tylko odczytać te pliki.
Na przykład do określania dostępności miejsca na dane są przydatne te metody:
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
Wybierz fizyczną lokalizację miejsca na dane
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 kilka fizycznych woluminów, które mogą zawierać zewnętrzną pamięć masową, więc musisz wybrać, którego z nich użyć do przechowywania danych aplikacji.
Aby uzyskać dostęp do różnych lokalizacji, wywołaj ContextCompat.getExternalFilesDirs()
.
Jak widać w fragmentie kodu, pierwszy element zwracanej tablicy jest uważany za podstawowy wolumin pamięci zewnętrznej. Używaj go, 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 plików stałych
Aby uzyskać dostęp do plików aplikacji z zewnętrznego miejsca na dane, zadzwoń pod numer getExternalFilesDir()
.
Aby zachować wydajność aplikacji, nie otwieraj i zamykaj tego samego pliku kilka razy.
Ten fragment kodu pokazuje, jak wywołać funkcję getExternalFilesDir()
:
Kotlin
val appSpecificExternalDir = File(context.getExternalFilesDir(null), filename)
Java
File appSpecificExternalDir = new File(context.getExternalFilesDir(null), filename);
Tworzenie plików pamięci podręcznej
Aby dodać plik związany z aplikacją do pamięci podręcznej w pamięci zewnętrznej, uzyskaj odwołanie do externalCacheDir
:
Kotlin
val externalCacheFile = File(context.externalCacheDir, filename)
Java
File externalCacheFile = new File(context.getExternalCacheDir(), filename);
Usuń pliki pamięci podręcznej
Aby usunąć plik z katalogu zewnętrznej pamięci podręcznej, użyj metody delete()
w obiekcie File
reprezentującym plik:
Kotlin
externalCacheFile.delete()
Java
externalCacheFile.delete();
Treści multimedialne
Jeśli Twoja aplikacja obsługuje pliki multimedialne, które są wartościowe dla użytkownika tylko w ramach aplikacji, najlepiej przechowywać je w katalogach aplikacji w pamięci zewnętrznej, jak widać 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, np. DIRECTORY_PICTURES
.
Te nazwy katalogów zapewniają prawidłowe traktowanie plików przez system.
Jeśli żadna z wstępnie zdefiniowanych nazw podkatalogów nie pasuje do Twoich plików, możesz zamiast tego podać parametr null
w parametrze getExternalFilesDir()
. Spowoduje to zwrócenie katalogu głównego aplikacji w pamięci zewnętrznej.
Zapytanie o wolne miejsce
Wielu użytkowników nie ma zbyt dużo miejsca na swoich urządzeniach, dlatego aplikacja powinna je wykorzystywać rozsądnie.
Jeśli wiesz z wyprzedzeniem, ile danych będziesz przechowywać, możesz sprawdzić, ile miejsca na urządzeniu może być udostępnione Twojej aplikacji, wywołując funkcję getAllocatableBytes()
.
Wartość zwrócona przez funkcję getAllocatableBytes()
może być większa niż bieżąca ilość wolnego miejsca na urządzeniu. Dzieje się tak, ponieważ system wykrył pliki, które może usunąć z katalogów pamięci podręcznej innych aplikacji.
Jeśli jest wystarczająco dużo miejsca na dane aplikacji, zadzwoń pod numer allocateBytes()
.
W przeciwnym razie aplikacja może poprosić użytkownika o usunięcie niektórych plików z urządzenia lub usunięcie z niego wszystkich plików pamięci podręcznej.
Ten fragment kodu pokazuje, jak aplikacja może wysyłać zapytania o wolne miejsce 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 związanej z zarządzaniem miejscem na dane
Aplikacja może deklarować i tworzyć niestandardową aktywność, która po uruchomieniu umożliwia użytkownikowi zarządzanie danymi przechowywanymi przez aplikację na jego urządzeniu. Deklarujesz tę niestandardową aktywność „zarządzanie pokojem” za pomocą atrybutu android:manageSpaceActivity
w pliku manifestu. Aplikacje menedżera plików mogą wywoływać tę aktywność nawet wtedy, gdy aplikacja nie eksportuje aktywności, czyli gdy aktywność ustawia 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ę zawierającą działanie ACTION_MANAGE_STORAGE
. Ten zamiar wyświetla użytkownikowi prompt. W razie potrzeby może też pokazywać ilość wolnego miejsca na urządzeniu. Aby wyświetlić takie informacje, skorzystaj z tego obliczenia:
StorageStatsManager.getFreeBytes() / StorageStatsManager.getTotalBytes()
Poproś użytkownika o usunięcie wszystkich plików z pamięci podręcznej
Możesz też poprosić użytkownika o wyczyszczenie plików pamięci podręcznej ze wszystkich aplikacji na urządzeniu. Aby to zrobić, wywołaj intencję zawierającą działanie intencji ACTION_CLEAR_APP_CACHE
.
Dodatkowe materiały
Więcej informacji o zapisywaniu plików w pamięci urządzenia znajdziesz w tych materiałach.