Solution 1 :
Since I was not able to find the actual code that I use usually I will just be guiding you through the process which should be more than enough.
As you can see the bitmap conversion also occurs in the UI thread (It is a heavy process, right?), this blocks the UI.
This is true Bitmaps, in general, are really heavy and can cause OOM without any doubts and hence handling them should be your utmost priority!, To handle bitmaps you need to do the following things that will optimize them to the maximum and really fast as well without blocking your UI.
-
Use the async methods to actually convert the bytes to bitmap using the following method:
var bmp = await BitmapFactory.DecodeByteArrayAsync(byteArray, 0, byteArray.Count(), options);
This will not do the decoding on the UI thread and not freeze your application, Also do not add this into your using state as that would dispose of it while it is still being referenced by your Image which will, in turn, throw an exception.
Also, the options do not need to be disposed of they are not heavy at all and can be GC’d when the time is right just leave it as you can actually even reuse this later if needed!
Also, the Bitmap’s recycle method recycles your bitmap ergo making it useless and REMEMBER if a UI element is using this bitmap you will be getting an object was disposed exception and the StackTrace sometimes will not be clear what caused it i.e. confusion for no apparent reason. The best way to handle this is only clean your Bitmaps when you know they are useless now i.e. on page level when you go to another page not when you are still using it, Also your page reappearing needs to be handled i.e. image should be reassigned so that the above exception is not thrown again!
Also, make sure you optimize your image bitmap to the size of the View holder i.e. make sure that your image is only supposed to be in which you can do with the below method
Solution 2 :
You can use AsyncTask to load and show image, AsyncTask do the task in another thread but will return to the UI thread once the task is done, code like this:
class BitmapWorkerTask : AsyncTask<byte[], int, Bitmap>
{
private MainActivity mainActivity;
public BitmapWorkerTask(MainActivity mainActivity)
{
this.mainActivity = mainActivity;
}
protected override Bitmap RunInBackground(params byte[][] @params)
{
return null;
}
protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] native_parms)
{
base.DoInBackground(native_parms);
BitmapFactory.Options options = new BitmapFactory.Options();
options.InJustDecodeBounds = false;
Thread.Sleep(4000);
return BitmapFactory.DecodeByteArray(mainActivity.imageData, 0, mainActivity.imageData.Length, options);
}
protected override void OnPostExecute(Bitmap result)
{
base.OnPostExecute(result);
mainActivity.imageView.SetImageBitmap(result);
}
}
and here is something in MainActivity:
ImageView imageView;
byte[] imageData;
BitmapWorkerTask workerTask;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
//initialize your byte array imageData here
imageView = (ImageView)FindViewById(Resource.Id.image);
workerTask = new BitmapWorkerTask(this);
workerTask.Execute();
}
Just in case if you don’t need to load the exact same size of the big picture, refer to this page for loading bitmap efficiently
Solution 3 :
Async & Await is the solution to your problem. AsyncAwait Features is introduced only to avoid problems related to Blocking UI due to the long-running process.
Problem :
I have a scenario of loading bitmap to a ImageView from a byteArray. I use the following code to achieve this.
Inside a class that extends ImageView
this.Post(() =>
{
using (customImage = BitmapFactory.DecodeByteArray(byteArray, 0, byteArray.Count(), options))
{
using (var renderBitemap = customImage.Copy(Bitmap.Config.Argb4444, true))
{
customImage?.Recycle();
options.Dispose();
options = null;
if (m_pdfParent.undisposedPageImages.Contains(m_pageIndex))
{
this.SetImageBitmap(renderBitemap);
}
stream = null;
}
});
As you can see the bitmap conversion also occurs in the UI thread (It is a heavy process, right?), this blocks the UI.
1) On using only the SetimageBitmap
method in the UI thread => I get an object disposed exception.
2) On removing the this.Post
and running everything in the background thread => I get change an exception that only the thread that created a view can alter the View.
Is there a way to improve this code snippet?(Setting bitmap from byteArray to an ImageView without blocking the UI thread)
Comments
Comment posted by FreakyAli
I would suggest you add the control that you have created i.e. the whole thing for us to understand what could be causing this exactly, Because if it says that “this” object was disposed of then its a problem!!
Comment posted by Nikhileshwar
The original class has nearly 500 lines of code. Code given in the code snippet works correctly, but the UI is blocked. Moreover the Object disposed exception is due to the
Comment posted by FreakyAli
I have an async method that I usually use to set Bitmaps to my controls will share that take a look if that would work for you!
Comment posted by Nikhileshwar
@FreakyAli, Could you please share the method
Comment posted by FreakyAli
Hey, man, I tried finding the code but I was not able to do so, Anyway, I will add a sort of idea of what I understood from your question and you should be able to solve your problem with that.
Comment posted by Nikhileshwar
I’m caught up in another work. I’ll try it and comment later. Sorry for the delay