在Xamarin.Android中将位图设置为来自byteArray的图像,并且UI线程上的负载最小

问题描述 投票:2回答:3

我有一种从byteArray将位图加载到ImageView的方案。我使用以下代码来实现这一目标。

在扩展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;
    }
});

您可以看到,位图转换也发生在UI线程中(这是一个繁重的过程,对吗?这会阻塞UI。

1] 仅在UI线程中使用SetimageBitmap方法打开 =>我得到一个对象处置异常。

2)删除this.Post并在后台线程中运行所有内容 =>我得到一个例外,即只有创建视图的线程才能更改视图。

有没有一种方法可以改进此代码段?(将位图从byteArray设置为ImageView而不阻塞UI线程)

c# android multithreading xamarin xamarin.android
3个回答
1
投票

异步并等待是您解决问题的方法。引入AsyncAwait功能只是为了避免由于长时间运行而导致与阻止UI相关的问题。


1
投票

您可以使用AsyncTask加载和显示图像,AsyncTask在另一个线程中执行任务,但是一旦任务完成,它将返回UI线程,如下代码:

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);
            }
        }

这是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();
        }

如果不需要加载完全相同的大尺寸图片,请参考此页面的loading bitmap efficiently


0
投票

由于我无法找到我通常使用的实际代码,因此我将指导您完成整个过程,这应该绰绰有余。

您可以看到,位图转换也发生在UI线程中(这是一个繁重的过程,对吗?这会阻塞UI。

这是正确的,通常来说,位图非常重,可以毫无疑问地导致OOM,因此,处理位图应该是您的最高优先级!,要处理位图,您需要执行以下操作以最大程度地优化它们。速度也很快,而且不会阻塞您的UI。

  • 使用异步方法使用以下方法将字节实际转换为位图:

    var bmp = await BitmapFactory.DecodeByteArrayAsync(byteArray, 0, byteArray.Count(), options);
    

这将不会在UI线程上进行解码,也不会冻结您的应用程序,也不要将其添加到您的using状态,因为当您的Image仍在引用它时,它将被丢弃,这将依次抛出一个例外。

此外,不需要处置这些选项,它们一点也不累赘,可以在适当的时候进行GC处理,因为您甚至可以在以后需要时重用它!

此外,Bitmap的recycle方法回收您的位图使它变得无用,并且REMEMBER如果UI元素正在使用该位图,您将得到对象被处置的异常,并且StackTrace有时不清楚导致它的原因,即没有明显原因的混乱。最好的处理方法是仅当您知道位图现在无用时才对其进行清理,即在转到另一页时在页面级别上,而不是在您仍在使用时对其进行清理,而且还需要处理重新出现的页面,即应重新分配图像,以便不会再次抛出上述异常!

此外,请确保将图像位图优化为View持有者的大小,即确保仅使用以下方法可以在其中使用图像

© www.soinside.com 2019 - 2024. All rights reserved.