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です。たまにはうまくいくのに、2枚目、3枚目の写真を撮りたいときはいつもOOMエラーになってしまう。

私がやっていることはメモリ集約的であることを認識しています。例えば ​​:

  1. 最初に、元の画像の初期の幅と高さが必要です。
  2. 次に、サンプリングされます(うまくできているかどうかはわかりません)。
  3. 次に、サンプリングしたビットマップをメモリにロードします。
  4. それをメモリにロードすると、最初のビットマップを Recycle() する前に、スケーリングされたビットマップもメモリにロードする必要があります。
  5. 最終的に、ビットマップを Web 経由で送信するために byte[] が必要です。しかし、スケーリングされたビットマップをリリースする前に、まずそれを変換する必要があります。
  6. 次に、スケーリングされたビットマップを解放し、byte[] を送信します。
  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 ドキュメント]
  • イメージビューの高さと幅に関連するビットマップ ターゲットの幅と高さを計算します。これは、MeasuredHeight および MeasuredWidth プロパティによって計算できます。[注: これは完全な画像描画後にのみ機能します]
  • メインスレッドで実行する代わりに、非同期メソッドを使用してファイルをデコードすることを検討してください [DecodeFileAsync]

詳細については、このhttp://appliedcodelog.blogspot.in/2015/07/avoiding-imagebitmap.htmlに従ってください

于 2015-07-24T06:32:48.257 に答える