Sobes.tech
Middle

How to properly transfer a large amount of data, such as an image, between activities in Android?

sobes.tech AI

Answer from AI

Passing large amounts of data, such as images, directly through Intent is not recommended due to the size limit on data serialized during transmission (usually a few hundred kilobytes or megabytes). Transferring too much data can lead to a TransactionTooLargeException.

Proper approaches:

  1. Save data to a file and pass the file path in Intent.

    • Save the image to a temporary file.
    • Pass the URI or file path in the Extra of the intent.
    • The receiving activity reads the file from the received path.
    // Saving in the first activity
    val imageUri: Uri = saveImageToFile(bitmap) // Function to save Bitmap to file and return Uri
    val intent = Intent(this, SecondActivity::class.java)
    intent.putExtra("image_uri", imageUri.toString())
    startActivity(intent)
    
    // Reading in the second activity
    val imageUriString = intent.getStringExtra("image_uri")
    if (imageUriString != null) {
        val imageUri = Uri.parse(imageUriString)
        val bitmap = loadImageFromFile(imageUri) // Function to load Bitmap from Uri
        imageView.setImageBitmap(bitmap)
    }
    
  2. Use Singleton or global data storage.

    • Place data in a Singleton object or a separate class accessible from anywhere in the app.
    • No need to pass data between activities, just access the Singleton to get the data.
    // Global object, e.g., in Application class or a separate singleton
    object DataHolder {
        var bitmap: Bitmap? = null
    }
    
    // In the first activity
    DataHolder.bitmap = bitmap
    
    // In the second activity
    val bitmap = DataHolder.bitmap
    imageView.setImageBitmap(bitmap)
    DataHolder.bitmap = null // Clear after use if needed
    

    Consider the app's lifecycle and activity recreation.

  3. Use a database (Room) or data storage (e.g., SharedPreferences, though not ideal for images).

    • Save the image (possibly as a byte array) in the database.
    • Pass the database record ID via Intent.
    • The receiving activity reads the image from the database using the ID.
    // Saving in the first activity (pseudo-code for Room)
    val imageEntity = ImageEntity(imageData = bitmapToByteArray(bitmap))
    val imageId = imageDatabase.imageDao().insertImage(imageEntity)
    val intent = Intent(this, SecondActivity::class.java)
    intent.putExtra("image_id", imageId)
    startActivity(intent)
    
    // Reading in the second activity (pseudo-code for Room)
    val imageId = intent.getLongExtra("image_id", -1)
    if (imageId != -1L) {
        val imageEntity = imageDatabase.imageDao().getImageById(imageId)
        if (imageEntity != null) {
            val bitmap = byteArrayToBitmap(imageEntity.imageData)
            imageView.setImageBitmap(bitmap)
        }
    }
    
  4. Use Shared ViewModel in Navigation Component.

    • If using Navigation Component, you can use a shared ViewModel tied to the navigation graph or activity.
    • Data is placed in the ViewModel of one activity and accessible from another.
    // In ViewModel
    class SharedViewModel : ViewModel() {
        val imageBitmap = MutableLiveData<Bitmap>()
    }
    
    // In the first activity (using Navigation Component)
    private val sharedViewModel: SharedViewModel by activityViewModels() // or by navGraphViewModels(R.id.nav_graph)
    sharedViewModel.imageBitmap.value = bitmap
    
    // In the second activity
    private val sharedViewModel: SharedViewModel by activityViewModels() // or by navGraphViewModels(R.id.nav_graph)
    sharedViewModel.imageBitmap.observe(viewLifecycleOwner) { bitmap ->
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap)
        }
    }
    

The method choice depends on the use case, data size, and app complexity. For images, saving to a file (and passing the URI) or using Singleton/ViewModel are often the most suitable and efficient solutions.