ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Follow publication

📸 Android: Comparing FileProvider vs. MediaStore + Top Interview Questions

In the world of modern android development, Jetpack Compose has revolutionized UI building, making it more intuitive and efficient. But what if you need a simple function to download, save, and share images within your app? Whether you are building a social media app, a gallery viewer, or just need to hand images dynamically, this guide will walk you through the comparison between FileProvider and MediaStore as well as suggest some of interview questions. Now let’s go 🔥

  • 📱Access app-specific files
  • 📂 What is Android FileProvider?
  • 🔄 FileProvider in a Multi-Module Android Project?
  • 📸 What is Android Media Store?
  • 🚨Common Error & Solution
  • 📌 Questions & Answers
  • 🎯 Conclusion

📱 Access app-specific files

In many cases, your app creates files that other apps don’t need to access, or shouldn’t access. The system provides the following locations for storing such app-specific files:

  • Internal storage directories: These directories include both a dedicated location for storing persistent files, and another location for storing cache data. The system prevents other apps from accessing these locations, and on Android 10 (API level 29) and higher, these locations are encrypted. These characteristics make these locations a good place to store sensitive data that only your app itself can access.
  • External storage directories: These directories include both a dedicated location for storing persistent files, and another location for storing cache data. Although it’s possible for another app to access these directories if that app has the proper permissions, the files stored in these directories are meant for use only by your app. If you specifically intend to create files that other apps should be able to access, your app should store these files in the shared storage part of external storage instead.

When the user uninstalls your app, the files saved in app-specific storage are removed. Because of this behavior, you shouldn’t use this storage to save anything that the user expects to persist independently of your app. For example, if your app allows users to capture photos, the user would expect that they can access those photos even after they uninstall your app. So you should instead use shared storage to save those types of files to the appropriate media collection.

Scoped Storage & Why It Matters in Android 10+ 🚀

In Android 10 (API 29) and above, Google introduced Scoped Storage to improve app security and user privacy. This change restricts how apps can access files on external storage. and MediaStore is the recommended way to interact with media files.

Before Scoped Storage, apps could freely read/write to Environment.getExternalStorageDirectory(), but this posed security risks since apps could access files from other apps.

Note: If your app requests a storage-related permission at runtime, the user-facing dialog indicates that your app is requesting broad access to external storage, even when scoped storage is enabled.

✅ What Scoped Storage Changes

Limited File Access

  • Apps can no longer freely access all files in external storage (/sdcard/).
  • Instead, apps can only access their own app-specific directories inside:
  • 📂 Android/data/com.example.app/
  • 📂 Android/media/com.example.app/

FileProvider is Required for Sharing

  • Direct file:// URIs are blocked (causes FileUriExposedException).
  • Apps must use FileProvider.getUriForFile() to share files.

Temporary Permissions for File Access

  • If an app wants to share a file (e.g., an image) with another app (WhatsApp, Gmail, etc.), it must use:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
  • This grants temporary permission for the receiving app to read the file.

Media Store APIs for Public Files

  • Apps should use MediaStore APIs to read/write public files (e.g., Photos, Downloads).

📂 What is Android FileProvider?

FileProvider is a special subclass of ContentProvider in Android that allows you to securely share files between different applications without exposing the internal storage file paths. It provides a way to generate content:// URIs instead of file:// URIs, which prevents issues with the Scoped Storage and File URI Exposure Exception on modern Android versions.

  • ✅ Securely share files between apps.
  • ✅ Prevent FileUriExposedException on Android 7.0+.
  • ✅ Works with camera intents, sharing files via email, social media, …
  • ✅ Supports multiple modules in a project.

📌 Basic Implementation of FileProvider

1️⃣ Specify the FileProvider in AndroidManifest.xml

Defining a FileProvider for your app requires an entry in your manifest. This entry specifies the authority to use in generating content URIs, as well as the name of an XML file that specifies the directories your app can share.

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">

<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />

</provider>

2️⃣ Specify sharable directories filepaths.xml in res/xml/

Once you have added the FileProvider to your app manifest, you need to specify the directories that contain the files you want to share. To specify the directories, start by creating the file filepaths.xml in the res/xml/ subdirectory of your project. In this file, specify the directories by adding an XML element for each directory. The following snippet shows you an example of the contents of res/xml/filepaths.xml. The snippet also demonstrates how to share a subdirectory of the files/ directory in your internal storage area:

<?xml version="1.0" encoding="utf-8"?>
<!-- Allow access to app-specific external storage -->

<paths>
<!-- 📂 /storage/emulated/0/Android/data/{package_name}/**/images/-->
<external-files-path
name="images"
path="images/" />

<files-path
name="myimages"
path="images/" />

</paths>

In this example:

  • external-files-path: Refers to the app's external storage
  • external-cache-path: Refers to the app's external cache directory
  • <files-path> tag shares directories within the files/ directory of your app's internal storage. The path attribute shares the images/ subdirectory of files/. The name attribute tells the FileProvider to add the path segment myimages to content URIs for files in the files/images/ subdirectory.
Example of using context.cacheDir

Note: The XML file is the only way you can specify the directories you want to share; you can’t programmatically add a directory.

3️⃣ Define the BuildConfig

  • BuildConfig
buildConfigField("String", "FILE_PROVIDER", "\"provider\"")
  • FileProvider

import androidx.core.content.FileProvider as AndroidFileProvider

interface FileProvider {

@Throws(Exception::class)
fun getUri(context: Context, file: File): Uri? {
val authority = authority(context)
return AndroidFileProvider.getUriForFile(context, authority, file)
}

companion object : FileProvider {
val authority = { context: Context ->
"${context.applicationContext.packageName}.${BuildConfig.FILE_PROVIDER}"
}
}

🆚 FileProvider in a Multi-Module Android Project?

By default, FileProvider is tied to an authority (e.g., com.example.app.fileprovider), which is unique per module. In a multi-module project, each module cannot define its own FileProvider with the same authority. Instead, follow these steps:

Approach 1: Declare FileProvider in the App Module

  • Allow feature modules to access FileProvider
  • Pass the Context of the main application to the modules.
  • Use FileProvider.getUriForFile(...) in the modules with the app’s authority (com.example.app.fileprovider).

Approach 2: Create a Shared Module for FileProvider

  • Create a new module (e.g., foundation-io).
  • Define the FileProvider in foundation-io and expose a method:
  • All modules will use this method to get the URI.

📊 Comparison of Approach 1 and Approach 2:

Use Approach 1 (App Module) if:

  • Your project is small or medium-sized.
  • You don’t plan to reuse FileProvider in multiple apps.
  • Simplicity is more important than modularity.

Use Approach 2 (Shared Module) if:

  • Your project is large, with multiple feature modules.
  • You want to decouple FileProvider from the main app.
  • You might reuse the shared module in other projects.
  • Your team follows clean architecture and modularization best practices.

If you’re working on a large-scale project with multiple modules (e.g., a super app, SDK-based architecture), Approach 2 (Shared Module) is the best choice because it promotes scalability, reusability, and maintainability. However, if you just need quick file sharing in a single app, Approach 1 (App Module) is simpler and works fine.

📸 What is Android Media Store?

The contract between the media provider and applications. Contains definitions for the supported URIs and columns. The media provider provides an indexed collection of common media types, such as Audio, Video, and Images, from any attached storage devices. Each collection is organized based on the primary MIME type of the underlying content; for example, image/* content is indexed under Images. The Files collection provides a broad view across all collections, and does not filter by MIME type.

1️⃣ Query Media Files (Images)

public void getAllImages(Context context) {
Uri collection = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;

String[] projection = {
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DISPLAY_NAME
};

Cursor cursor = context.getContentResolver().query(
collection,
projection,
null, null,
MediaStore.Images.Media.DATE_ADDED + " DESC"
);

if (cursor != null) {
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID));
String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME));

Uri imageUri = Uri.withAppendedPath(collection, String.valueOf(id));
Log.d("MediaStore", "Image: " + name + ", URI: " + imageUri);
}
cursor.close();
}
}

2️⃣ Insert a New Image

public Uri saveImageToGallery(Context context, Bitmap bitmap, String filename) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, filename);
values.put(MediaStore.Images.Media.MIME_TYPE, "image/png");
values.put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/MyApp");

Uri imageUri = context.getContentResolver().insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

try {
OutputStream outputStream = context.getContentResolver().openOutputStream(imageUri);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream);
outputStream.close();
return imageUri;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}

📝 Note:

  • The RELATIVE_PATH specifies where the image is stored (Pictures/MyApp).
  • No WRITE_EXTERNAL_STORAGE permission needed from Android 10+.

3️⃣ Delete a Media File

public boolean deleteImage(Context context, Uri uri) {
int deleted = context.getContentResolver().delete(uri, null, null);
return deleted > 0;
}

🔹 Permissions Needed:

  • Android 10+: No storage permission required, as long as using MediaStore.
  • Android 9 and below: Requires WRITE_EXTERNAL_STORAGE permission.

4️⃣ Update Media File Metadata

public void updateMediaDetails(Context context, Uri uri, String newTitle) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, newTitle);

context.getContentResolver().update(uri, values, null, null);
}

The system automatically scans an external storage volume and adds media files to the following well-defined collections:

  • Images, including photographs and screenshots, which are stored in the DCIM/ and Pictures/ directories. The system adds these files to the MediaStore.Images table.
  • Videos, which are stored in the DCIM/, Movies/, and Pictures/ directories. The system adds these files to the MediaStore.Video table.
  • Audio files, which are stored in the Alarms/, Audiobooks/, Music/, Notifications/, Podcasts/, and Ringtones/ directories. Additionally, the system recognizes audio playlists that are in the Music/ or Movies/ directories as well as voice recordings that are in the Recordings/ directory. The system adds these files to the MediaStore.Audio table. The Recordings/ directory isn't available on Android 11 (API level 30) and lower.
  • Downloaded files, which are stored in the Download/ directory. On devices that run Android 10 (API level 29) and higher, these files are stored in the MediaStore.Downloads table. This table isn't available on Android 9 (API level 28) and lower.

🚨Common Error & Solution

1️⃣ SecurityException: Permission Denied

java.lang.SecurityException: Permission Denial: writing Uri content://media/external/images/media from pid=12345, uid=10085 requires android.permission.WRITE_EXTERNAL_STORAGE

Cause:

  • Your app is targeting Android 10+ and trying to write to shared storage without using MediaStore.
  • You did not request WRITE_EXTERNAL_STORAGE (Android 9 and below).

Solution:

  • ✅ Use MediaStore API for Android 10+ instead of direct file paths.
  • No permission required for public media directories on Android 11+.
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "my_image.jpg");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);

Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);

2️⃣ File Not Found Exception

java.io.FileNotFoundException: open failed: ENOENT (No such file or directory)

Cause:

  • The file path is incorrect or does not exist.
  • You are trying to access a file before it has been saved.

Solution:

  • Verify the file exists before accessing it.
  • ✅ Ensure you have write permissions if using direct storage access.

3️⃣ Image Not Appearing in Gallery

Cause: Media file is saved, but not indexed by MediaScanner.

Solution:Manually scan the file after saving.

MediaScannerConnection.scanFile(context,
new String[]{file.getAbsolutePath()},
new String[]{"image/jpeg"},
(path, uri) -> Log.d("MediaScanner", "File Scanned: " + path));

4️⃣ FileUriExposedException (Android 7+)

Direct file paths (file://) are not allowed for sharing files across apps in Android 7+.

android.os.FileUriExposedException: file:///storage/emulated/0/my_file.pdf exposed beyond app through ClipData.Item.getUri()

Solution: ✅ Use FileProvider to generate a content URI instead.

Uri uri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", new File(filePath));

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(uri, "application/pdf");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);

5️⃣ android:authorities Not Matching

java.lang.IllegalArgumentException: Couldn't find meta-data for provider with authority com.example.wrong.fileprovider

Cause: The authority name in AndroidManifest.xml does not match the one used in FileProvider.getUriForFile().

Solution: ✅ Ensure the android:authorities in AndroidManifest.xml matches exactly.

6️⃣ Permission Denied When Opening a Shared File

java.lang.SecurityException: Permission Denial: reading Uri content://com.example.myapp.fileprovider/my_files/test.pdf

Cause: The receiving app does not have permission to read the file.

Solution:Grant temporary read permissions when sharing a file.

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

7️⃣ Failed to find configured root that contains ….

/storage/emulated/0/Pictures/..

The error “Failed to find configured root that contains…” usually occurs when trying to share a file without proper FileProvider configuration. In Android 7.0 (API 24) and above, direct file:// URIs cannot be shared between apps due to security restrictions. Instead, you need to use a content:// URI via FileProvider.

  • Inside the <application> tag:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">

<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />

</provider>
  • file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!-- Allow access to app-specific external storage -->
<external-files-path name="pictures" path="Pictures/" />
</paths>
  • Code
// App-specific external storage
val picturesDir = File(context.getExternalFilesDir(null), "Pictures")
picturesDir.mkdirs() // Ensure the directory exists

📌 Questions & Answers

1️⃣ What is MediaStore in Android?

MediaStore is an API that provides structured access to media files (images, videos, audio) stored on external storage. It allows querying, inserting, and updating media files without requiring direct file path access.

2️⃣ How much space does your data require?

Internal storage has limited space for app-specific data. Use other types of storage if you need to save a substantial amount of data.

3️⃣ How reliable does data access need to be?

If your app’s basic functionality requires certain data, such as when your app is starting up, place the data within internal storage directory or a database. App-specific files that are stored in external storage aren’t always accessible because some devices allow users to remove a physical device that corresponds to external storage.

4️⃣ What are the advantages of using MediaStore over File API?

No storage permission required (Android 10+).
Works with scoped storage (File API does not).
Structured access to media files.
Auto indexing by the system.

5️⃣ What is FileProvider in Android?

FileProvider is a content provider (ContentProvider) that allows secure file sharing between apps using content:// URIs instead of file:// paths.

7️⃣ Should the data be private to your app?

When storing sensitive data — data that shouldn’t be accessible from any other app — use internal storage, preferences, or a database. Internal storage has the added benefit of the data being hidden from users.

8️⃣ What is Scoped Storage in Android?

Scoped Storage (introduced in Android 10) restricts direct access to shared storage and requires apps to use MediaStore or FileProvider.

9️⃣ How do you access files in Scoped Storage?

  • For media files: Use MediaStore API
  • For app-specific files: Use getExternalFilesDir()
  • For file sharing: Use FileProvider

To give users more control over their files and limit file clutter, Android 10 introduced a new storage paradigm for apps called scoped storage. Scoped storage changes the way apps store and access files on a device’s external storage. To help you migrate your app to support scoped storage, follow the best practices for common storage use cases that are outlined in this guide. The use cases are organized into two categories: handling media files and handling non-media files.

🎯 Conclusion

By leveraging Jetpack Compose,FileProvider, and Kotlin, we can easily download, store, and share images in an Android app. This approach ensures smooth performance, proper file handling, and seamless sharing across different applications. Whether you are working on an image-heavy app or simply adding a sharing feature, mastering these techniques will enhance your development workflow 🚀💡

🔹 MediaStore (Scoped Storage API)

Purpose:

  • MediaStore is part of the Scoped Storage model introduced in Android 10 (API 29).
  • It provides access to public media files (e.g., images, videos, and audio) stored in shared storage, without needing READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permissions (from Android 11 onwards).

Use Cases:

  • Saving media files (images, videos, audio) in public directories (e.g., Pictures/, Movies/).
  • Querying and managing media content using the ContentResolver API.
  • Ensuring compliance with Scoped Storage, as direct file path access is restricted.

Advantages:

  • ✅ No need for storage permissions (from Android 11+).
  • ✅ Works with Scoped Storage, avoiding direct file access.
  • ✅ Provides a content URI, making it safer for media handling.
  • ✅ Compatible with MediaScanner for indexing media files.

Disadvantages:

  • ❌ Cannot store non-media files (e.g., PDFs, ZIPs).
  • ❌ Requires querying APIs (instead of direct file access)
  • ❌ Files are not private — they’re accessible to all apps with read access to shared storage.

🔹FileProvider (For Secure File Sharing)

Purpose:

  • FileProvider is a special ContentProvider that allows an app to share private files securely with other apps using a content URI, rather than exposing raw file paths.

Use Cases:

  • Sharing files (PDFs, images, videos, etc.) with other apps (e.g., email, social media, or messaging apps).
  • Providing a secure way to expose app-private storage files (inside data/data/<package>/files).
  • Granting temporary read/write access to specific files.

Advantages:

  • Secure file sharing (avoids exposing raw file paths).
  • ✅ Supports any file type, not just media files.
  • ✅ Provides temporary access permissions using FLAG_GRANT_READ_URI_PERMISSION.
  • ✅ Works with intent-based sharing (e.g., Intent.ACTION_SEND).

Disadvantages:

  • ❌ Requires defining a FileProvider in AndroidManifest.xml.
  • ❌ Needs XML configuration for file paths.
  • More complex setup compared to direct file sharing.

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

Published in ProAndroidDev

The latest posts from Android Professionals and Google Developer Experts.

Written by Leo N

🇻🇳 🇸🇬 🇲🇾 🇦🇺 🇹🇭 Engineer @ GXS Bank, Singapore | MSc 🎓 | Technical Writer . https://github.com/nphausg

No responses yet

Write a response