How should images be passed from data layer to UI layer in a multi-module Android app? [closed]

3 weeks ago 41
ARTICLE AD BOX

I am working on a project that strictly follows a multi-module, feature-based layered architecture. The app is divided into features, and each feature has its own UI, Domain, and Data layers.

The UI layer depends on the Domain layer, which is a pure Kotlin module and follows Clean Architecture principles. The Domain layer contains only use cases and repository interfaces and does not depend on any Android-specific APIs.

The Data layer implements these repository interfaces, so it depends on the Domain layer. Both the UI layer and Data layer are Android library modules, but the Domain layer is a pure Kotlin library, meaning no Android classes can be used there.

The problem is that the Data layer currently holds a list of images as Bitmap, which are loaded from local storage using the MediaStore API. Since Bitmap is an Android-specific class, it cannot be exposed to or referenced in the Domain layer.

So, how can the UI layer receive and display these images from the Data layer through the Domain layer without violating Clean Architecture principles?

Snippet of folder structure
Folder structure of feature module which have clean architecure

Code snippet

private val semaphore = Semaphore(2) // it required due to i do async operation and without it MediaMetaData throw error and crash app. suspend fun getImageVideoThumbnail( context: Context, uri: Uri, size: Size = Size(200, 200) ): Bitmap? = withContext(Dispatchers.IO) { var bitmap: Bitmap? = null val cr = context.contentResolver try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { bitmap = cr.loadThumbnail(uri, size, null) } else { cr.query(uri, null, null, null, null)?.use { cursor -> val idCol = cursor.getColumnIndexOrThrow(MediaStore.Video.Media._ID) while (cursor.moveToNext()) { val id = cursor.getLong(idCol) bitmap = MediaStore.Video.Thumbnails.getThumbnail( cr, id, MediaStore.Video.Thumbnails.MINI_KIND, null ) break } } } bitmap } catch (e: Exception) { // e.printStackTrace() getFrame(context , uri , size) } } suspend fun getFrame(context: Context, uri: Uri , size : Size): Bitmap? = withContext(Dispatchers.IO) { semaphore.withPermit { val retriever = MediaMetadataRetriever() try { retriever.setDataSource(context, uri) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { retriever.getScaledFrameAtTime( 0, MediaMetadataRetriever.OPTION_CLOSEST_SYNC, size.width, size.height ) } else { retriever.frameAtTime?.let { src -> src.scale(size.width, size.height).also { src.recycle() } } } } catch (e: Exception) { null } finally { retriever.release() } } }

What i want?

I want a clean approach that allows the UI layer to receive a list of images without breaking the integrity of the Domain layer.

One idea I am considering is to convert Bitmap to ByteArray in the Data layer, pass this ByteArray through the Domain layer, and then reconstruct the Bitmap in the UI layer. This keeps Android-specific classes out of the Domain layer.

Is this a good approach, or is there a better way to handle this architectural problem?

What is the best way to solve this issue while still following Clean Architecture principles?

Read Entire Article