0

开发人员,我寻求您的帮助。

我有一个与内存有关的问题。我真的不知道如何解决这个问题,所以我只会展示我的代码片段。请记住,虽然它在 Xamarin.Android 上,但它也适用于普通 Android。

我使用一个刚刚启动相机意图的库。我正在使用的库(或组件)是:http ://components.xamarin.com/view/xamarin.mobile 。这并不真正相关,但也许你可以指出我为什么应该或不应该使用这个库的其他见解。

无论如何,启动相机并捕获输入。我使用以下代码:

    private void StartCamera() {
        var picker = new MediaPicker (this);

        if (! picker.IsCameraAvailable) {

            Toast.MakeText (this, "No available camera found!", ToastLength.Short).Show ();

        } else {
            var intent = picker.GetTakePhotoUI (new StoreCameraMediaOptions{
                Name = "photo.jpg",
                Directory = "photos"
            });

            StartActivityForResult (intent, 1);

        }
    }

当我从此相机意图返回时,将调用 onActivityForResult() 方法。在这种方法中,我执行以下操作:

    protected override async void OnActivityResult (int requestCode, Result resultCode, Intent data)
    {
        // User canceled
        if (resultCode == Result.Canceled)
            return;

        System.GC.Collect ();

        dialog = new ProgressDialog (this);
        dialog.SetProgressStyle (ProgressDialogStyle.Spinner);
        dialog.SetIconAttribute (Android.Resource.Attribute.DialogIcon);
        dialog.SetTitle (Resources.GetString(Resource.String.dialog_picture_sending_title));
        dialog.SetMessage (Resources.GetString(Resource.String.dialog_picture_sending_text));
        dialog.SetCanceledOnTouchOutside (false);
        dialog.SetCancelable (false);
        dialog.Show ();

        MediaFile file = await data.GetMediaFileExtraAsync (this);

        await ConvertingAndSendingTask ( file );

        dialog.Hide();

        await SetupView ();

    }

然后,在我的 ConvertingAndSendingTask() 中,我使用缩放位图将图片转换为所需的尺寸。代码如下:

public async Task ConvertingAndSendingTask(MediaFile file) {

        try{

            System.GC.Collect();

            int targetW = 1600;
            int targetH = 1200;

            BitmapFactory.Options options = new BitmapFactory.Options();
            options.InJustDecodeBounds = true;
            Bitmap b = BitmapFactory.DecodeFile (file.Path, options);

            int photoW = options.OutWidth;
            int photoH = options.OutHeight;

            int scaleFactor = Math.Min(photoW/targetW, photoH/targetH);

            options.InJustDecodeBounds = false;
            options.InSampleSize       = scaleFactor;
            options.InPurgeable        = true;

            Bitmap bitmap = BitmapFactory.DecodeFile(file.Path, options);

            float resizeFactor = CalculateInSampleSize (options, 1600, 1200);

            Bitmap bit = Bitmap.CreateScaledBitmap(bitmap, (int)(bitmap.Width/resizeFactor),(int)(bitmap.Height/resizeFactor), false);

            bitmap.Recycle();

            System.GC.Collect();

            byte[] data = BitmapToBytes(bit);

            bit.Recycle();

            System.GC.Collect();

            await app.api.SendPhoto (data, app.ChosenAlbum.foreign_id);

            bitmap.Recycle();

            System.GC.Collect();


        } catch(Exception e) {

            System.Diagnostics.Debug.WriteLine (e.StackTrace);

        }




    }

好吧,这种方法在具有更多内存的较新设备上发送它很好,但在低端设备上它最终会出现内存不足错误。或者无论如何几乎是OOM。当Somethimes我做得很好但当我想拍第二张或第三张照片时,它总是以OOM错误告终。

我意识到我正在做的是内存密集型的。例如 :

  1. 首先我想要原始图像的初始宽度和高度。
  2. 然后对其进行采样(我真的不知道它是否做得好)。
  3. 然后我将采样的位图加载到内存中。
  4. 当我将它加载到内存中时,我的缩放位图也必须在 Recycle() 第一个位图之前加载到内存中。
  5. 最终我需要一个字节 [] 来通过网络发送位图。但我需要先转换它,然后再发布我的缩放位图。
  6. 然后我释放我的缩放位图并发送字节 []。
  7. 然后作为最后一步,还需要从内存中释放 byte[]。我已经在我的 BitmapToBytes() 方法上这样做了,如下所示,但我想将它包括在内以获取其他见解。

    static byte[] BitmapToBytes(Bitmap bitmap) {
        byte[] data = new byte[0];
        using (MemoryStream stream = new MemoryStream ()) 
        {
            bitmap.Compress (Bitmap.CompressFormat.Jpeg, 90, stream);
            stream.Close ();
            data = stream.ToArray ();
        }
        return data;
    }
    

有人看到我可以优化这个过程的任何好的部分吗?我知道我正在将很多内容加载到内存中,但我想不出另一种方式。

应该提到的是,我总是希望我的图像是 1600x1200(横向)或 1200x1600(纵向)。我通过以下方式计算该值:

public static float CalculateInSampleSize(BitmapFactory.Options options,
                                            int reqWidth, int reqHeight) {
        // Raw height and width of image
        int height = options.OutHeight;
        int width = options.OutWidth;
        float inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            // Calculate ratios of height and width to requested height and
            // width
            float heightRatio = ((float) height / (float) reqHeight);
            float widthRatio = ((float) width / (float) reqWidth);

            // Choose the smallest ratio as inSampleSize value, this will
            // guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }

        if(height < reqHeight || width < reqWidth) {
            // Calculate ratios of height and width to requested height and
            // width
            float heightRatio = ((float) reqHeight / (float) height);
            float widthRatio =  ((float) reqWidth / (float) width);

           // Choose the smallest ratio as inSampleSize value, this will
           // guarantee
           // a final image with both dimensions larger than or equal to the
           // requested height and width.
           inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }

        return inSampleSize;
    }

有人有任何建议或替代工作流程吗?

我会在这方面得到很大帮助!

4

1 回答 1

2

这可能是一个非常延迟的回复,但可能对遇到相同问题的人有所帮助。

  • 使用计算的 InSampleSize 值(选项)将文件解码为位图。这本身会生成按比例缩小的位图,而不是在 CreateScaledBitmap 中使用。
  • 如果您期望高分辨率图像作为输出,则很难处理 OutOfMemory 问题。因为具有更高分辨率的图像不会提供任何可见的好处,但仍会占用宝贵的内存并由于视图执行的额外缩放而导致额外的性能开销。[xamarin 文档]
  • 计算与imageview高度和宽度相关的位图目标宽度和高度。您可以通过 MeasuredHeight 和 MeasuredWidth 属性来计算。[注意:这仅在完成图像绘制后才有效]
  • 考虑使用异步方法解码文件,而不是在主线程上运行 [DecodeFileAsync]

有关更多详细信息,请遵循此http://appliedcodelog.blogspot.in/2015/07/avoiding-imagebitmap.html

于 2015-07-24T06:32:48.257 回答