DEV Community

loading...
Cover image for Persisting Images Across Activity's LifeCycle Using SharedPreferences in Android | Kotlin

Persisting Images Across Activity's LifeCycle Using SharedPreferences in Android | Kotlin

Aayush Gupta
A 22 y.o. Android Developer from Bhilai who also happens to be an avid FOSS Contributor
・3 min read

Images are an important part of applications and there might be times when one might want them to persist across the activity/application's lifecycle. In this article, I will show you that how we can save the images which have been loaded in an ImageView widget can be saved using the SharedPrefernces library as a base64 string but also how we can load it again.

Saving Image

In order to save the image as a base64 string, we require an instance of SharedPreferences.

val sharedPref = context.getSharedPreferences("sharedPref", Context.MODE_PRIVATE)
Enter fullscreen mode Exit fullscreen mode

Now, we need to grab the loaded image from the ImageView widget, convert it into a base64 string. This can be done by firstly converting the image into a bitmap, then using the compress method over it to write it to a ByteArrayOutputStream.

Writing the image into a ByteArrayOutputStream allows us to call toByteArray method over the stream which converts into a ByteArrary.

This ByteArray is later accepted as an argument Base64.encodeToString method which will finally convert the image into a base64 string.

val baos = ByteArrayOutputStream()
val bitmap = imageView.drawable.toBitmap()
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos)
val encodedImage = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT)
Enter fullscreen mode Exit fullscreen mode

Now as we have converted the image into a base64 string, we need to save it using the SharedPrefernces library instance which we created earlier.

with(sharedPref.edit()) {
    putString("encodedImage", encodedImage)
    apply()
}
Enter fullscreen mode Exit fullscreen mode

This will create a new key with the name encodedImage and store the value of the variable with the same name in it.

Here is how the complete function looks in my case:

package dev.theimpulson.imaged.backend

import android.content.Context
import android.graphics.Bitmap
import android.util.Base64
import android.widget.ImageView
import androidx.core.graphics.drawable.toBitmap
import java.io.ByteArrayOutputStream

class ImageManagement {

    companion object {

        fun saveImage(imageView: ImageView, context: Context) {
            /**
             * Saves the current image from the imageView widget
             * in MainActivity
             */
            val sharedPref = context.getSharedPreferences("sharedPref", Context.MODE_PRIVATE)

            val baos = ByteArrayOutputStream()
            val bitmap = imageView.drawable.toBitmap()
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos)
            val encodedImage = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT)

            with(sharedPref.edit()) {
                putString("encodedImage", encodedImage)
                apply()
            }
        }

    }
}
Enter fullscreen mode Exit fullscreen mode

Loading Image

Now as we have saved an image in the last section, we will see how we can easily load it back into the ImageView as well.

To get the previously saved string, we require an instance of SharedPreferences and call the getString method on it.

val sharedPref = context.getSharedPreferences("sharedPref", Context.MODE_PRIVATE)
val encodedImage = sharedPref.getString("encodedImage", "DEFAULT")
Enter fullscreen mode Exit fullscreen mode

This will get us the previously saved base64 string of the image it exists otherwise it will return a string containing "DEFAULT" characters. This is useful considering we would only like to decode the image if it actually contains a base64 string.

Now we will use the Base64.decode function and pass our base64 string and Base64.DEFAULT as arguments. This will return us a ByteArray through which we will obtain the image's bitmap again.

To get the image's bitmap, we will now use BitmapFactory.decodeByteArray function which will take our previously decoded ByteArray, offset (which we will keep at 0), and length of the ByteArray (which we will obtain by calling size method on the respective array).

Once we have the bitmap, we can use setImageBitmap method on the ImageView and pass our bitmap to load it.

if (encodedImage != "DEFAULT") {
    val imageBytes = Base64.decode(encodedImage, Base64.DEFAULT)
    val decodedImage = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
    imageView.setImageBitmap(decodedImage)
}
Enter fullscreen mode Exit fullscreen mode

Here is how the complete function looks in my case:

package dev.theimpulson.imaged.backend

import android.content.Context
import android.graphics.BitmapFactory
import android.util.Base64
import android.widget.ImageView

class ImageManagement {

    companion object {

        fun loadLastImage(imageView: ImageView, context: Context) {
            /**
             * Loads the last saved image from SharedPreferences into the
             * MainActivity's ImageView
             */
            val sharedPref = context.getSharedPreferences("sharedPref", Context.MODE_PRIVATE)
            val encodedImage = sharedPref.getString("encodedImage", "DEFAULT")

            if (encodedImage != "DEFAULT") {
                val imageBytes = Base64.decode(encodedImage, Base64.DEFAULT)
                val decodedImage = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
                imageView.setImageBitmap(decodedImage)
            }
        }

    }
}
Enter fullscreen mode Exit fullscreen mode

and that's all. Now you can safely save and load an image as a base64 string to make it persist across the activity's lifecycle.

Discussion (1)

Collapse
wolfheros profile image
James Huker

Care about the performance, saving and loading.