15

いくつかの写真を撮り、サイズを変更してバックエンドサーバーに送信するAndroidアプリがあります。このアプリは、Samsung Galaxy S3を除く他のすべての電話(ジンジャーブレッドとアイスクリームサンドイッチ)で完全に正常に動作します。写真を撮ってサイズを変更しようとすると、メモリが不足します。最初はサイズ変更の問題だと思い、insamplesizeでビットマップオブジェクトを実装してすべてを試しましたが、それでもクラッシュし続けました。その後、アプリを最初に起動したときに、大量のメモリを消費していることに気付きました。HTCや他のすべての電話では、アプリを起動すると約4 MBを消費しますが、GalaxyS3では約30MGを消費します。これは約10倍です。何が原因でそれだけ多くのメモリを消費するのか理解できないようです。私はすべてのオブジェクトとレイアウトを適切にリサイクルしています(私は思います)。

public static void resizeImage(String pathOfInputImage,
        String pathOfOutputImage) {
    try {
        int inWidth = 0;
        int inHeight = 0;
        float factor;

        InputStream in = new FileInputStream(pathOfInputImage);

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(in, null, options);
        inWidth = options.outWidth;
        inHeight = options.outHeight;

        in.close();
        in = null;

        options = null;         

        if (inWidth > inHeight) {
            factor = (float) inHeight / 480;
        } else {
            factor = (float) inWidth / 480;
        }           
        options = new BitmapFactory.Options();

        Bitmap roughBitmap;

        if(Build.VERSION.SDK_INT < 12) {
            System.gc();
        }

        try
        {
            in = new FileInputStream(pathOfInputImage);
            options.inSampleSize = 4;
            roughBitmap = BitmapFactory.decodeStream(in, null, options);
        }
        catch (OutOfMemoryError e)
        {
                roughBitmap = Utils.fetchRoughBitmap(pathOfInputImage, factor);
            }
        finally
        {
            roughBitmap = Utils.fetchRoughBitmap(pathOfInputImage, factor);
            }

        in.close();
        in = null;

        boolean rotate = false;
        Bitmap rotatedBitmap = null;

        if (isNeedToRotate(pathOfInputImage)) {
            rotate = true;
            Matrix m = new Matrix();

            m.postRotate(90);
            try
            {
                rotatedBitmap = Bitmap.createBitmap(roughBitmap, 0, 0,
                        roughBitmap.getWidth(), roughBitmap.getHeight(), m,
                        true);
            }
            catch (OutOfMemoryError e)
            {
                rotatedBitmap = Bitmap.createBitmap(roughBitmap, 0, 0,
                        roughBitmap.getWidth()/2, roughBitmap.getHeight()/2, m,
                        true);
            }
        }


        if (rotate) {
            Utils.log(TAG, "Rotate Invoked :------->");
            int temp = inHeight;

            inHeight = inWidth;
            inWidth = temp;
        }

        options.inSampleSize = Math.round(factor);

        float dstWidth = (int) inWidth / factor;
        float dstHeight = (int) inHeight / factor;

        Bitmap resizedBitmap;
        if (rotate) {
            try
            {
                resizedBitmap = Bitmap.createScaledBitmap(rotatedBitmap,
                        (int) dstWidth, (int) dstHeight, true);
            }
            catch (OutOfMemoryError e)
            {
                resizedBitmap = Bitmap.createScaledBitmap(rotatedBitmap,
                        (int) dstWidth/2, (int) dstHeight/2, true);
            }

        } else {

            try
            {
                resizedBitmap = Bitmap.createScaledBitmap(roughBitmap,
                    (int) dstWidth, (int) dstHeight, true);
            }
            catch (OutOfMemoryError e)
            {
                resizedBitmap = Bitmap.createScaledBitmap(roughBitmap,
                        (int) dstWidth/2, (int) dstHeight/2, true);
            }
        }

        try 
        {
            FileOutputStream out = new FileOutputStream(pathOfOutputImage);
            resizedBitmap.compress(Bitmap.CompressFormat.PNG, 80, out);
        }
        catch (Exception e) {
            Utils.log("Image", e.getMessage() + e);
        }

        //Free up the memory.
        roughBitmap.recycle();
        resizedBitmap.recycle();

        if (rotatedBitmap != null) {
            rotatedBitmap.recycle();
        }

    } catch (IOException e) {
        Utils.log("Image", e.getMessage() + e);
    }
}


private static Bitmap fetchRoughBitmap(String pathOfInputImage, float factor)
{
    Bitmap roughBitmap = null;
    try
    {
        FileInputStream in = new FileInputStream(pathOfInputImage);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = Math.round(factor);
        roughBitmap = BitmapFactory.decodeStream(in, null, options);
        return roughBitmap;
    }
    catch (OutOfMemoryError mem_error)
    {
        return Utils.fetchRoughBitmap(pathOfInputImage, factor+1);
    }
    catch (FileNotFoundException e)
    {
        //return Utils.fetchRoughBitmap(pathOfInputImage, factor+1);
        return roughBitmap;
    }
}

このコードは、メモリが不足しなくなるまで再帰ループを実行するため、正常に機能しますが、結果の画像は約300x300ピクセルであり、少なくとも640x480である必要があります。私の質問は次のとおりです。

  1. Galaxy S3がHTC電話で60%ではなく、アプリを最初にロードしたときに最大90%のメモリを使用する理由を誰かに教えてもらえますか?
  2. アプリのメモリが不足する原因となっているサイズ変更コードで何か問題がありますか?

私はすでにこの問題に3日間費やしているので、どんな助けでも大歓迎です:-(。

4

4 に答える 4

9

うーん... 古い質問であることはわかっていますが、これが私の考えです。

Galaxy S3はxhdpi密度を使用するため、起動時に大きなヒープサイズを割り当てます(私が知る限り、32MB)

ただし、res ディレクトリに xhdpi リソースがない場合は、drawable 内または xhdpi 密度に最も近い hdpi からデフォルトの画像を呼び出します。

次に、必要に応じて画像を拡張します。この段階で、画像を拡張すると多くのメモリが消費され、メモリ不足の問題が発生する可能性があります。

画面互換モードを有効にすることでこの問題を解決するか、(デフォルトで存在しない場合) drawable-xhdpi を作成して、Galaxy S3 などの xhdpi デバイスに適切なリソースを提供することができます。

于 2012-11-07T05:29:59.260 に答える
8

わお。私たちは、S3 でのこのメモリ不足の問題に何週間も悩まされてきました。最後に、このスレッドのテクニックの 1 つを適用しました。

私たちのアプリでは、drawable-hdpi にアプリのすべての画像を含めました。ほとんどの電話では、問題はありませんでした。S3 では、アプリは 2 倍のメモリを消費し、メモリ不足の問題が発生します。

drawable-hdpi フォルダーと同じ内容の drawable-xhdpi フォルダーを作成し、S3 で実行しました。メモリ フットプリントが 1/2 であり、メモリ不足の問題がないことにすぐに気付きました。

これはイライラする経験でした。願わくば、他の人がこのスレッドを探してデガグの時間を節約することを知っていることを願っています。

ありがとう

于 2013-10-31T19:26:59.093 に答える
5

私は自分で問題を解決しました。メモリが不足しているアプリは、上記のコードで何もする必要はありませんでした。MAT を追加したところ、以前のビューからビットマップ オブジェクトが維持されていることがわかりました。そのため、ビューからグラフィックを削除すると、メモリの問題が解決しました。

于 2012-09-11T20:09:38.157 に答える
0
I overwrote the onDestroy method and added the following line: layout.setBackgroundDrawable(null);. Please let me know if this worked or didn't work for you. 

– ユーザー881148

かっこいい、それは私にとってはうまくいきました...レイアウトの背景写真に少し追加しました。BitmapDrawables または Bitmap を使用する代わりに、ビットマップから変換した後に Drawables を使用します。

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.outerspace);
Drawable bitDraw = new BitmapDrawable(getResources(), bitmap);

main.setBackgroundDrawable(bitDraw);

私の問題は、S3を除くすべてのデバイスで動作していたことです(これは面倒でした)。しかし、アプリは現在稼働中です!ありがとう... user881148

于 2013-04-02T01:37:42.070 に答える