I am implementing a grid gallery in my Android app in which photos are sorted according to the photo's Exif metadata. All photos are read from a specific folder stored in Shared Storage at /DCIM
or /Pictures
. Since the photos will be sorted by their Exif metadata, the Exif metadata must be known before displaying the photos in a RecyclerView
.
Currently, I read the files using a content provider, as recommended in this link. My code works well and is fast, but I also need to read the Exif metadata for each photo. My current implementation uses an InputStream
to extract metadata via ExifInterface
, which slows down the code to about 20ms per photo, compared to under 1ms per photo when excluding the metadata. Due to this considerable slowdown, displaying hundreds of photos is not acceptable in terms of the user experience.
Here is my current implementation:
private fun getMediaWithExif(folderName: String): List<Photo> {
val photoList = mutableListOf<Photo>()
val imagesUri: Uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(
MediaStore.Images.Media._ID,
MediaStore.Images.Media.DATE_MODIFIED,
MediaStore.Images.Media.DISPLAY_NAME,
)
val bucketNameSelection = MediaStore.Images.Media.BUCKET_DISPLAY_NAME + " = ? "
val selection = bucketNameSelection
val selectionArgs: Array<String> = arrayOf(folderName)
val sortOrder = MediaStore.Images.ImageColumns.DATE_ADDED + " DESC"
appContext.contentResolver.query(
imagesUri,
projection,
selection,
selectionArgs,
sortOrder
)?.use { cursor ->
val idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID)
val dateColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATE_MODIFIED)
val nameColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME)
while (cursor.moveToNext()) {
val id = cursor.getLong(idColumn)
val date = cursor.getLong(dateColumn)
val name = cursor.getString(nameColumn)
val contentUri: Uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id)
appContext.contentResolver.openInputStream(contentUri)?.use { stream ->
ExifInterface(stream).run {
photoList.add(
Photo(
uri = contentUri,
name = name,
date = Date(date * 1000),
latitude = this.latLong?.get(0),
longitude = this.latLong?.get(1),
accuracy = this.getAttribute(ExifInterface.TAG_GPS_H_POSITIONING_ERROR)
?.toFloatOrNull(),
altitude = this.getAltitude(0.0),
)
)
}
}
}
}
return photoList
}
I am seeking suggestions on how to efficiently read photos, including Exif metadata. Is there a better approach to extracting Exif metadata, or should I periodically check Shared Storage for modifications and cache any photo data into a ROOM database for readily available photo data?
Note that I have tried initialising the ExifInterface
with an absolute path String
, File
, and a FileDescriptor
, but it did not speed up the code. Thank you for your time and help, it is greatly appreciated!