Android 4.4
KitKat
URI
Intent.ACTION_GET_CONTENT
Android Gallery

Android Gallery on Android 4.4 KitKat returns different URI for Intent.ACTION_GET_CONTENT

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

On Android 4.4 KitKat, ACTION_GET_CONTENT often returns a different kind of Uri than older gallery code expected. The important change is that KitKat introduced the Storage Access Framework and document-style content:// URIs became much more common, so code that assumes a filesystem path starts breaking.

Why the URI Looks Different on KitKat

Before KitKat, many apps expected media picker results to look like classic MediaStore content URIs or even file paths. On Android 4.4, the picker may return document-provider URIs such as:

  • 'content://com.android.providers.media.documents/document/image:12345'
  • 'content://com.android.externalstorage.documents/document/primary:Pictures/demo.jpg'

That does not mean the URI is wrong. It means the platform is exposing content through document providers instead of promising direct filesystem access.

Do Not Convert Everything to a File Path

The old instinct was to turn the returned Uri into a raw path string and then open the file directly. That approach is fragile on KitKat and becomes even less reliable on newer Android versions.

The safer pattern is to treat the result as a content URI and use ContentResolver.

kotlin
1val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
2    type = "image/*"
3}
4startActivityForResult(intent, 1001)

Then read the result as a stream:

kotlin
1override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
2    super.onActivityResult(requestCode, resultCode, data)
3
4    if (requestCode == 1001 && resultCode == Activity.RESULT_OK) {
5        val uri = data?.data ?: return
6
7        contentResolver.openInputStream(uri)?.use { input ->
8            val bytes = input.readBytes()
9            println("Read ${bytes.size} bytes")
10        }
11    }
12}

This works regardless of whether the URI points to MediaStore, a documents provider, or another content source.

If You Need Metadata, Query the Resolver

Sometimes you need the display name or MIME type rather than the raw bytes. Query the resolver instead of parsing the URI manually.

kotlin
1val uri: Uri = data?.data ?: return
2
3contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { cursor ->
4    val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
5    if (cursor.moveToFirst() && nameIndex != -1) {
6        val displayName = cursor.getString(nameIndex)
7        println(displayName)
8    }
9}

This is much more reliable than trying to reconstruct a path from the URI authority and document ID.

Understanding DocumentsContract

On KitKat and later, document URIs can be inspected with DocumentsContract if you really need provider-specific handling.

kotlin
1if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(this, uri)) {
2    val documentId = DocumentsContract.getDocumentId(uri)
3    println(documentId)
4}

That can help when you need to recognize a provider authority or branch into special handling. But even then, the best default is still “open through ContentResolver,” not “force a filesystem path.”

ACTION_GET_CONTENT Versus ACTION_OPEN_DOCUMENT

ACTION_GET_CONTENT gives you temporary access to user-selected content. If you need longer-lived access that survives beyond the immediate interaction, ACTION_OPEN_DOCUMENT is often the better API because it works with persistable URI permissions.

That distinction matters because many old gallery examples were really trying to solve a document-access problem, not a one-time content selection problem.

Why Old Path-Based Helpers Fail

Many older Android snippets looked up a _data column from MediaStore and treated it as the image path. That approach becomes unreliable because:

  • not every provider exposes a _data column
  • the content may not map cleanly to a local file path
  • future platform versions increasingly discourage path-based assumptions

If your real goal is to upload, decode, or copy the image, you usually do not need the path at all.

Common Pitfalls

The biggest mistake is assuming a returned KitKat URI must be turned into a filesystem path before use. In many cases, that is unnecessary and brittle.

Another mistake is parsing provider-specific URI strings manually instead of opening the content through ContentResolver.

Developers also confuse temporary access from ACTION_GET_CONTENT with persistable document access. Those are different contracts.

Finally, do not assume code written for the pre-KitKat gallery model still matches the storage model introduced by the Storage Access Framework.

Summary

  • KitKat often returns document-style content:// URIs for ACTION_GET_CONTENT.
  • That change is normal and comes from the Storage Access Framework.
  • Treat the returned value as a content URI and use ContentResolver.
  • Avoid converting the URI into a raw filesystem path unless you have a very specific provider-aware reason.
  • If you need long-term access, consider ACTION_OPEN_DOCUMENT instead of ACTION_GET_CONTENT.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions