Руководство Android External Storage
View more Tutorials:
Android External Storage: это внешнее хранилище Android, к файлам которые вы храните здесь не применяется система безопасности.
Обычно имеется 2 вида внешнего хранилища (external storage).
- Фиксированное внешнее хранилище: Обычно понимается как жесткий диск телефона.
- Съемное хранилище (Removeable Storage): Например SD Card.
Используя статический метод класса Environment вы можете получить информацию папки внешнего хранилища.
Это таблица результатов при запуске на эмуляторе (emulator):
Method | Returns |
getDataDirectory() | /data |
getDownloadCacheDirectory() | /cache |
getExternalStorageState() | mounted |
getExternalStoragePublicDirectory(Environment.Music): | /storage/emulated/0/Music |
getDownloadCacheDirectory() | /cache |
getRootDirectory() | /system |
Чтобы прочитать/записать данные на внешнем хранилище, требуется конфигурация AndroidManifest.xml, добавьте:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
С Android Level >= 23 чтобы прочитать/записать данные на внешнем хранилище, вам нужно спросить пользователя, используя код. (Смотрите в примере).
Android API Level < 29
С Android API Level < 29, приложения могут храгить свои данные во внешнем хранилище (External Storage), точнее в папке ниже (или в подпапках).
if (android.os.Build.VERSION.SDK_INT < 29) { // ==> /storage/emulated/0 (Emulator) File dir = Environment.getExternalStorageDirectory(); }
Метод хранения упомянутый выше может привести к конфликту между разными приложениями, потому что все эти приложения могут хранить свои данные в одной и той же папке (или подпапках), более того если вы удалите (uninstall) одно приложение, данные созданные этим приложением будут так же существовать во внешнем хранилище.
Android API Level 29+
С Android API 29+, каждое приложение будет хранить свои данные в разных папках во внешнем хранилище. Когда пользователь удаляет приложение, все созданные им данные так же будут удалены.
if (android.os.Build.VERSION.SDK_INT >= 29) { // ==> /storage/emulated/0/Android/data/org.o7planning.externalstoragedemo/files File dir = this.getExternalFilesDir(null); }

Данный сlass может быть вам полезным при работе с внешим хранилищем:
ExternalStorageUtils
package org.o7planning.externalstoragedemo.utils; import android.content.Context; import android.os.Build; import android.os.Environment; import android.os.StatFs; import androidx.annotation.RequiresApi; import java.io.File; public class ExternalStorageUtils { // Check whether the external storage is mounted or not. public static boolean isExternalStorageMounted() { String dirState = Environment.getExternalStorageState(); if(Environment.MEDIA_MOUNTED.equals(dirState)) { return true; }else { return false; } } // Check whether the external storage is read only or not. public static boolean isExternalStorageReadOnly() { String dirState = Environment.getExternalStorageState(); if(Environment.MEDIA_MOUNTED_READ_ONLY.equals(dirState)) { return true; }else { return false; } } // Get private external storage base directory. public static String getPrivateExternalStorageBaseDir(Context context, String dirType) { String ret = ""; if(isExternalStorageMounted()) { File file = context.getExternalFilesDir(dirType); ret = file.getAbsolutePath(); } return ret; } // Get private cache external storage base directory. public static String getPrivateCacheExternalStorageBaseDir(Context context) { String ret = ""; if(isExternalStorageMounted()) { File file = context.getExternalCacheDir(); ret = file.getAbsolutePath(); } return ret; } // Get public external storage base directory. public static String getPublicExternalStorageBaseDir() { String ret = ""; if(isExternalStorageMounted()) { File file = Environment.getExternalStorageDirectory(); ret = file.getAbsolutePath(); } return ret; } // Get public external storage base directory. public static String getPublicExternalStorageBaseDir(String dirType) { String ret = ""; if(isExternalStorageMounted()) { File file = Environment.getExternalStoragePublicDirectory(dirType); ret = file.getAbsolutePath(); } return ret; } // Get external storage disk space, return MB @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public static long getExternalStorageSpace() { long ret = 0; if (isExternalStorageMounted()) { StatFs fileState = new StatFs(getPublicExternalStorageBaseDir()); // Get total block count. long count = fileState.getBlockCountLong(); // Get each block size. long size = fileState.getBlockSizeLong(); // Calculate total space size ret = count * size / 1024 / 1024; } return ret; } // Get external storage left free disk space, return MB @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public static long getExternalStorageLeftSpace() { long ret = 0; if (isExternalStorageMounted()) { StatFs fileState = new StatFs(getPublicExternalStorageBaseDir()); // Get free block count. long count = fileState.getFreeBlocksLong(); // Get each block size. long size = fileState.getBlockSizeLong(); // Calculate free space size ret = count * size / 1024 / 1024; } return ret; } // Get external storage available disk space, return MB @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2) public static long getExternalStorageAvailableSpace() { long ret = 0; if (isExternalStorageMounted()) { StatFs fileState = new StatFs(getPublicExternalStorageBaseDir()); // Get available block count. long count = fileState.getAvailableBlocksLong(); // Get each block size. long size = fileState.getBlockSizeLong(); // Calculate available space size ret = count * size / 1024 / 1024; } return ret; } }
Create a project named ExternalStorageDemo.
- File > New > New Project > Empty Activity
- Name: ExternalStorageDemo
- Package name: org.o7planning.externalstoragedemo
- Language: Java

AndroidManifest.xml configuration allows read and write data on the external storage memory.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Full content of AndroidManifest.xml:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="org.o7planning.externalstoragedemo"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
The application interface:

Если вас интересуют шаги дизайна интерфейса данного приложения, смотрите приложение в конце статьи.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <EditText android:id="@+id/editText" android:layout_width="0dp" android:layout_height="48dp" android:layout_marginStart="16dp" android:layout_marginLeft="16dp" android:layout_marginTop="19dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:ems="10" android:inputType="textPersonName" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <LinearLayout android:id="@+id/linearLayout" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:orientation="horizontal" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/editText"> <Button android:id="@+id/button_save" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0" android:text="Save" /> <Button android:id="@+id/button_read" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0" android:text="Read" /> <Button android:id="@+id/button_list" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0" android:text="List Dirs" /> </LinearLayout> <TextView android:id="@+id/textView" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginStart="16dp" android:layout_marginLeft="16dp" android:layout_marginTop="16dp" android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:layout_marginBottom="16dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/linearLayout" /> </androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
package org.o7planning.externalstoragedemo; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import android.Manifest; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Environment; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import android.widget.Toast; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; public class MainActivity extends AppCompatActivity { private EditText editText; private TextView textView; private Button saveButton; private Button readButton; private Button listButton; private static final String LOG_TAG = "ExternalStorageDemo"; private static final int REQUEST_ID_READ_PERMISSION = 100; private static final int REQUEST_ID_WRITE_PERMISSION = 200; private final String fileName = "note.txt"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText = (EditText) findViewById(R.id.editText); textView = (TextView) findViewById(R.id.textView); saveButton = (Button) findViewById(R.id.button_save); readButton = (Button) findViewById(R.id.button_read); listButton = (Button) findViewById(R.id.button_list); saveButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { askPermissionAndWriteFile(); } }); readButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { askPermissionAndReadFile(); } }); listButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { listExternalStorages(); } }); } private void askPermissionAndWriteFile() { boolean canWrite = this.askPermission(REQUEST_ID_WRITE_PERMISSION, Manifest.permission.WRITE_EXTERNAL_STORAGE); if(!canWrite) { Toast.makeText(getApplicationContext(), "You do not allow this app to write files.", Toast.LENGTH_LONG).show(); return; } // this.writeFile(); } private void askPermissionAndReadFile() { boolean canRead = this.askPermission(REQUEST_ID_READ_PERMISSION, Manifest.permission.READ_EXTERNAL_STORAGE); // if (!canRead) { Toast.makeText(getApplicationContext(), "You do not allow this app to read files.", Toast.LENGTH_LONG).show(); return; } // this.readFile(); } // With Android Level >= 23, you have to ask the user // for permission with device (For example read/write data on the device). private boolean askPermission(int requestId, String permissionName) { Log.i(LOG_TAG, "Ask for Permission: " + permissionName); Log.i(LOG_TAG, "Build.VERSION.SDK_INT: " + android.os.Build.VERSION.SDK_INT); if (android.os.Build.VERSION.SDK_INT >= 23) { // Check if we have permission int permission = ActivityCompat.checkSelfPermission(this, permissionName); Log.i(LOG_TAG, "permission: " + permission); Log.i(LOG_TAG, "PackageManager.PERMISSION_GRANTED: " + PackageManager.PERMISSION_GRANTED); if (permission != PackageManager.PERMISSION_GRANTED) { // If don't have permission so prompt the user. this.requestPermissions( new String[]{permissionName}, requestId ); return false; } } return true; } // As soon as the user decides, allows or doesn't allow. @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); // // Note: If request is cancelled, the result arrays are empty. if (grantResults.length > 0) { switch (requestCode) { case REQUEST_ID_READ_PERMISSION: { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { readFile(); } } case REQUEST_ID_WRITE_PERMISSION: { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { writeFile(); } } } } else { Toast.makeText(getApplicationContext(), "Permission Cancelled!", Toast.LENGTH_SHORT).show(); } } // IMPORTANT!! public File getAppExternalFilesDir() { if (android.os.Build.VERSION.SDK_INT >= 29) { // /storage/emulated/0/Android/data/org.o7planning.externalstoragedemo/files return this.getExternalFilesDir(null); } else { // @Deprecated in API 29. // /storage/emulated/0 return Environment.getExternalStorageDirectory(); } } private void writeFile() { try { File extStore = this.getAppExternalFilesDir( ); boolean canWrite = extStore.canWrite(); Log.i(LOG_TAG, "Can write: " + extStore.getAbsolutePath()+" : " + canWrite); // ==> /storage/emulated/0/note.txt (API < 29) // ==> /storage/emulated/0/Android/data/org.o7planning.externalstoragedemo/files/note.txt (API >=29) String path = extStore.getAbsolutePath() + "/" + fileName; Log.i(LOG_TAG, "Save to: " + path); String data = editText.getText().toString(); Log.i(LOG_TAG, "Data: " + data); File myFile = new File(path); FileOutputStream fOut = new FileOutputStream(myFile); fOut.write(data.getBytes("UTF-8")); fOut.close(); Toast.makeText(getApplicationContext(), fileName + " saved", Toast.LENGTH_LONG).show(); } catch (Exception e) { Toast.makeText(getApplicationContext(), "Write Error:" + e.getMessage(), Toast.LENGTH_LONG).show(); Log.e(LOG_TAG, "Write Error: " + e.getMessage()); e.printStackTrace(); } } private void readFile() { File extStore = this.getAppExternalFilesDir(); // ==> /storage/emulated/0/note.txt (API < 29) // ==> /storage/emulated/0/Android/data/org.o7planning.externalstoragedemo/note.txt (API >=29) String path = extStore.getAbsolutePath() + "/" + fileName; Log.i(LOG_TAG, "Read file: " + path); String s = ""; String fileContent = ""; try { File myFile = new File(path); FileInputStream fIn = new FileInputStream(myFile); BufferedReader myReader = new BufferedReader( new InputStreamReader(fIn)); while ((s = myReader.readLine()) != null) { fileContent += s + "\n"; } myReader.close(); this.textView.setText(fileContent); } catch (IOException e) { Toast.makeText(getApplicationContext(), "Read Error:" + e.getMessage(), Toast.LENGTH_LONG).show(); Log.e(LOG_TAG, "Read Error: " + e.getMessage()); e.printStackTrace(); } Toast.makeText(getApplicationContext(), fileContent, Toast.LENGTH_LONG).show(); } private void listExternalStorages() { StringBuilder sb = new StringBuilder(); sb.append("Data Directory: ").append("\n - ") .append(Environment.getDataDirectory().toString()).append("\n"); sb.append("Download Cache Directory: ").append("\n - ") .append(Environment.getDownloadCacheDirectory().toString()).append("\n"); sb.append("External Storage State: ").append("\n - ") .append(Environment.getExternalStorageState().toString()).append("\n"); sb.append("External Storage Directory: ").append("\n - ") .append(Environment.getExternalStorageDirectory().toString()).append("\n"); sb.append("Is External Storage Emulated?: ").append("\n - ") .append(Environment.isExternalStorageEmulated()).append("\n"); sb.append("Is External Storage Removable?: ").append("\n - ") .append(Environment.isExternalStorageRemovable()).append("\n"); sb.append("External Storage Public Directory (Music): ").append("\n - ") .append(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).toString()).append("\n"); sb.append("Download Cache Directory: ").append("\n - ") .append(Environment.getDownloadCacheDirectory().toString()).append("\n"); sb.append("Root Directory: ").append("\n - ") .append(Environment.getRootDirectory().toString()).append("\n"); Log.i(LOG_TAG, sb.toString()); this.textView.setText(sb.toString()); } }
Приложение сохранит файл в SD Card эмулятора (Emulator), поэтому удостоверьтесь что на вашем устройстве настроен SD Card.
Running app:

See more about "Device File Explorer".
Добавьте EditText и Button в интерфейс.


Добавьте 1 TextView в интерфейс.


Настроить ID, Text для компонентов на интерфейсе:
