仲間の開発者、私はあなたの助けを求めています。
メモリに関連する問題があります。これに取り組む方法がよくわからないので、コードスニペットを提示します。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です。たまにはうまくいくのに、2枚目、3枚目の写真を撮りたいときはいつもOOMエラーになってしまう。
私がやっていることはメモリ集約的であることを認識しています。例えば :
- 最初に、元の画像の初期の幅と高さが必要です。
- 次に、サンプリングされます(うまくできているかどうかはわかりません)。
- 次に、サンプリングしたビットマップをメモリにロードします。
- それをメモリにロードすると、最初のビットマップを Recycle() する前に、スケーリングされたビットマップもメモリにロードする必要があります。
- 最終的に、ビットマップを Web 経由で送信するために byte[] が必要です。しかし、スケーリングされたビットマップをリリースする前に、まずそれを変換する必要があります。
- 次に、スケーリングされたビットマップを解放し、byte[] を送信します。
次に、最後のステップとして、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;
}
推奨事項や代替ワークフローはありますか?
私はこれでとても助かります!