开发人员,我寻求您的帮助。
我有一个与内存有关的问题。我真的不知道如何解决这个问题,所以我只会展示我的代码片段。请记住,虽然它在 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错误告终。
我意识到我正在做的是内存密集型的。例如 :
- 首先我想要原始图像的初始宽度和高度。
- 然后对其进行采样(我真的不知道它是否做得好)。
- 然后我将采样的位图加载到内存中。
- 当我将它加载到内存中时,我的缩放位图也必须在 Recycle() 第一个位图之前加载到内存中。
- 最终我需要一个字节 [] 来通过网络发送位图。但我需要先转换它,然后再发布我的缩放位图。
- 然后我释放我的缩放位图并发送字节 []。
然后作为最后一步,还需要从内存中释放 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;
}
有人有任何建议或替代工作流程吗?
我会在这方面得到很大帮助!