Understanding Android Scoped Storage and Migration Strategies
This article explains the concept and core principles of Android Scoped Storage, compares traditional file‑path, MediaStore and SAF access methods, and provides detailed migration solutions for media and non‑media files, compatibility modes, and the special MANAGE_EXTERNAL_STORAGE permission.
Background – Android 10 introduced Scoped Storage to give users better control over their files, restricting how apps access external storage and creating new adaptation challenges.
What is Scoped Storage? Scoped Storage (also called Scoped Storage) separates external files into media and non‑media categories, each with different access rules, and aims to let users manage their files more cleanly.
Android Storage Areas – Android devices have three storage zones: external, internal, and system. External storage is further divided into private and public spaces.
Android Storage Mechanisms
Three common ways to access files:
File‑path access (direct I/O streams) – used before Android 10.
MediaStore – a media‑library API for indexed media files.
Storage Access Framework (SAF) – a unified UI for browsing and opening documents without needing permissions.
Using MediaStore
fun savePicture(context: Context, file: File?) {
file?.let {
val values = ContentValues()
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
values.put(MediaStore.MediaColumns.DISPLAY_NAME, file.name)
values.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM)
val uri = context.contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
if (uri != null) {
val outputStream = context.contentResolver.openOutputStream(uri)
if (outputStream != null) {
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
outputStream.close()
}
}
}
}MediaStore does not require extra permissions for files created by the app, but reading other apps' shared files needs read/write permissions.
Using SAF
// Open system file picker
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
// intent.setType("image/*"); // optional filter
startActivityForResult(intent, FILE_CODE); private final String[] PROJECTION = { MediaStore.Images.Media.DISPLAY_NAME };
@Override
public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
if (requestCode == FILE_CODE && resultCode == Activity.RESULT_OK) {
Uri uri = null;
if (resultData != null) {
uri = resultData.getData();
Cursor cursor = this.getContentResolver()
.query(uri, PROJECTION, null, null, null, null);
}
}
}Scoped Storage Migration Solutions
Media files should be stored in app‑private or shared media directories and managed via MediaStore. Example for taking a photo and saving it:
public static File createTmpFile(Context context) {
// Use media directory
dir = context.getExternalFilesDir(DIRECTORY_PICTURES);
return File.createTempFile(JPEG_FILE_PREFIX, JPEG_FILE_SUFFIX, dir);
} // Open camera and save picture
private fun openCamera() {
val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (intent.resolveActivity(requireActivity().packageManager) != null) {
mCameraFile = createTmpFile(activity)
if (mCameraFile != null && mCameraFile!!.exists()) {
val cameraFileUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(requireContext(), requireContext().packageName + ".fileprovider", mCameraFile!!)
} else {
Uri.fromFile(mCameraFile)
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraFileUri)
startActivityForResult(intent, PHOTO_FROM_CAMERA_REQUEST_CODE)
}
}
}For non‑media files, direct path access to external public directories is prohibited. Two options are recommended: store them in the app‑private directory or use SAF.
private fun startDownload(fileUrl: String, fileName: String, context: Context) {
// Store in private external files dir
val file = File(context.getExternalFilesDir(), fileName)
// download logic omitted
DownloadTask.Builder(fileUrl, file)
.setFilename(fileName)
.start()
}
fun installApk(apkPath: String, context: Context) {
val file = File(apkPath)
if (file.exists()) {
val apkUri = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Uri.fromFile(file)
} else {
FileProvider.getUriForFile(context, "${context.packageName}.provider", file)
}
context.startActivity(Intent().apply {
action = "android.intent.action.VIEW"
addCategory("android.intent.category.DEFAULT")
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
setDataAndType(apkUri, "application/vnd.android.package-archive")
})
}
}Direct File‑Path Access for Media Files (Android 11+) – Android 11 allows path access to shared media directories, but performance may be slower than MediaStore; using MediaStore is recommended.
Compatibility Mode – Apps can stay in legacy mode by setting targetSdkVersion ≤ 28 or adding android:requestLegacyExternalStorage="true" for targetSdkVersion 29, though this flag is ignored on Android 11+.
<manifest ...>
<application android:requestLegacyExternalStorage="true" ...>
...
</application>
</manifest>All‑Files Access Permission (MANAGE_EXTERNAL_STORAGE) – Required for file managers or antivirus apps to read/write any file on external storage. Declare the permission, then launch the system settings intent.
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
if (!Environment.isExternalStorageManager()) {
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(intent);
}Google restricts this permission to special app categories; misuse may block app publishing.
Recommendations
Store newly created files in the app’s private directories; no extra permissions are needed.
Migrate existing files: move media files to MediaStore collections and non‑media files to private directories.
Summary – Scoped Storage provides a safer, more organized way to handle files on Android devices. Understanding its principles, the three access methods, and the migration steps ensures smooth adaptation across Android 10, 11 and later.
Recruitment Notice – The article concludes with a campus recruitment announcement for Snowball R&D, including a link to the application page.
Snowball Engineer Team
Proactivity, efficiency, professionalism, and empathy are the core values of the Snowball Engineer Team; curiosity, passion, and sharing of technology drive their continuous progress.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.