How can I pass a Bitmap object from one activity to another
Solution 1
Bitmap
implements Parcelable
, so you could always pass it with the intent:
Intent intent = new Intent(this, NewActivity.class);
intent.putExtra("BitmapImage", bitmap);
and retrieve it on the other end:
Intent intent = getIntent();
Bitmap bitmap = (Bitmap) intent.getParcelableExtra("BitmapImage");
Solution 2
Actually, passing a bitmap as a Parcelable will result in a "JAVA BINDER FAILURE" error. Try passing the bitmap as a byte array and building it for display in the next activity.
I shared my solution here:
how do you pass images (bitmaps) between android activities using bundles?
Solution 3
Passsing bitmap as parceable in bundle between activity is not a good idea because of size limitation of Parceable(1mb). You can store the bitmap in a file in internal storage and retrieve the stored bitmap in several activities. Here's some sample code.
To store bitmap in a file myImage in internal storage:
public String createImageFromBitmap(Bitmap bitmap) {
String fileName = "myImage";//no .png or .jpg needed
try {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
FileOutputStream fo = openFileOutput(fileName, Context.MODE_PRIVATE);
fo.write(bytes.toByteArray());
// remember close file output
fo.close();
} catch (Exception e) {
e.printStackTrace();
fileName = null;
}
return fileName;
}
Then in the next activity you can decode this file myImage to a bitmap using following code:
//here context can be anything like getActivity() for fragment, this or MainActivity.this
Bitmap bitmap = BitmapFactory.decodeStream(context.openFileInput("myImage"));
Note A lot of checking for null and scaling bitmap's is ommited.
Solution 4
If the image is too large and you can't save&load it to the storage, you should consider just using a global static reference to the bitmap (inside the receiving activity), which will be reset to null on onDestory, only if "isChangingConfigurations" returns true.
Solution 5
Compress and Send Bitmap
The accepted answer will crash when the Bitmap
is too large. I believe it's a 1MB limit. The Bitmap
must be compressed into a different file format such as a JPG represented by a ByteArray
, then it can be safely passed via an Intent
.
Implementation
The function is contained in a separate thread using Kotlin Coroutines because the Bitmap
compression is chained after the Bitmap
is created from an url String
. The Bitmap
creation requires a separate thread in order to avoid Application Not Responding (ANR) errors.
Concepts Used
- Kotlin Coroutines notes.
- The Loading, Content, Error (LCE) pattern is used below. If interested you can learn more about it in this talk and video.
- LiveData is used to return the data. I've compiled my favorite LiveData resource in these notes.
- In Step 3,
toBitmap()
is a Kotlin extension function requiring that library to be added to the app dependencies.
Code
1. Compress Bitmap
to JPG ByteArray
after it has been created.
Repository.kt
suspend fun bitmapToByteArray(url: String) = withContext(Dispatchers.IO) {
MutableLiveData<Lce<ContentResult.ContentBitmap>>().apply {
postValue(Lce.Loading())
postValue(Lce.Content(ContentResult.ContentBitmap(
ByteArrayOutputStream().apply {
try {
BitmapFactory.decodeStream(URL(url).openConnection().apply {
doInput = true
connect()
}.getInputStream())
} catch (e: IOException) {
postValue(Lce.Error(ContentResult.ContentBitmap(ByteArray(0), "bitmapToByteArray error or null - ${e.localizedMessage}")))
null
}?.compress(CompressFormat.JPEG, BITMAP_COMPRESSION_QUALITY, this)
}.toByteArray(), "")))
}
}
ViewModel.kt
//Calls bitmapToByteArray from the Repository
private fun bitmapToByteArray(url: String) = liveData {
emitSource(switchMap(repository.bitmapToByteArray(url)) { lce ->
when (lce) {
is Lce.Loading -> liveData {}
is Lce.Content -> liveData {
emit(Event(ContentResult.ContentBitmap(lce.packet.image, lce.packet.errorMessage)))
}
is Lce.Error -> liveData {
Crashlytics.log(Log.WARN, LOG_TAG,
"bitmapToByteArray error or null - ${lce.packet.errorMessage}")
}
}
})
}
2. Pass image as ByteArray
via an Intent
.
In this sample it's passed from a Fragment to a Service. It's the same concept if being shared between two Activities.
Fragment.kt
ContextCompat.startForegroundService(
context!!,
Intent(context, AudioService::class.java).apply {
action = CONTENT_SELECTED_ACTION
putExtra(CONTENT_SELECTED_BITMAP_KEY, contentPlayer.image)
})
3. Convert ByteArray
back to Bitmap
.
Utils.kt
fun ByteArray.byteArrayToBitmap(context: Context) =
run {
BitmapFactory.decodeByteArray(this, BITMAP_OFFSET, size).run {
if (this != null) this
// In case the Bitmap loaded was empty or there is an error I have a default Bitmap to return.
else AppCompatResources.getDrawable(context, ic_coinverse_48dp)?.toBitmap()
}
}
Related videos on Youtube
michael
Updated on January 02, 2020Comments
-
michael over 4 years
In my activity, I create a
Bitmap
object and then I need to launch anotherActivity
, How can I pass thisBitmap
object from the sub-activity (the one which is going to be launched)? -
slayton over 12 yearsIf the bitmap exists as a file or a resource, its is always better to pass the
URI
orResourceID
of the bitmap and not the bitmap itself. Passing the entire bitmap requires a lot of memory. Passing the URL requires very little memory and allows each activity to load and scale the bitmap as they need it. -
Houssem over 11 yearsDoesn't work for me, but this one do : stackoverflow.com/questions/11010386/…
-
Nathan Hutton over 10 yearsI tried this, didn't work. I followed the link, and it appears you should have used
CommonResources.photoFinishBitmap
instead ofConstants.photoFinishBitmap
. -
WantIt almost 10 years@slayton how do we pass images as URI / ResourceIDs? example? thanks!
-
AITAALI_ABDERRAHMANE almost 9 yearsputting bitmap on extra like that, is not a best practice, if the bitmap object size is bigger, then you get "java.lang.SecurityException: Unable to find app for caller android.app.ApplicationThreadProxy......". the recommended way is like @slayton says, you have to save bitmap on external storage and pass just the URI.
-
Alex almost 8 yearsBad practice. What will be happens with static field in the Activity class during recreation of the the whole process (for example, due to changing permissions for the app in runtime)? The answer is NPE.
-
AtifSayings about 6 yearswhat is max size of bitmap that can be passed?
-
Hawklike over 4 yearsThis will not compile - cannot resolve method
openFileOutput
. -
EAS over 2 years
android.os.TransactionTooLargeException
thrown with this.