decodebytearray - injustdecodebounds bitmap android




Bitmapオブジェクトにイメージをロードする際にメモリ不足の問題が発生する (20)

私は、各行に2つの画像ボタンを備えたリストビューを持っています。 リスト行をクリックすると、新しいアクティビティが起動されます。 私はカメラのレイアウトの問題のために自分のタブを構築しなければならなかった。 結果のために起動されるアクティビティはマップです。 私のボタンをクリックしてイメージプレビューを起動すると(SDカードからイメージを読み込む)、アプリケーションはアクティビティからlistviewアクティビティに戻って結果ハンドラに戻り、イメージウィジェット以上の新しいアクティビティを再起動します。

リストビューの画像プレビューは、カーソルとListAdapterで実行されています。 これは非常にシンプルですが、私はどのようにサイズ変更された画像を置くことができないのか分かりません(つまり、画像のボタンのsrcとしてPixelではない小さなビットサイズをオンザフライで表示します。

問題は、2回目の活動に戻って再起動しようとすると、メモリ不足エラーが発生することです。

  • リストアダプターを行単位で簡単に作成できる方法はありますか?その場でサイズを変更することは可能ですか?

これは、フォーカス問題のためにタッチスクリーンで行を選択することができないため、各行のウィジェット/要素のプロパティにいくつかの変更を加える必要があるので、これが望ましいでしょう。 ( 私はローラーボールを使うことができます

  • 帯域外のサイズ変更やイメージの保存ができますが、それは実際にやりたいことではありませんが、そのためのサンプルコードがうまくいくでしょう。

リストビューで画像を無効にすると、すぐに正常に機能しました。

参考:これは私がそれをやっていた方法です:

String[] from = new String[] { DBHelper.KEY_BUSINESSNAME,DBHelper.KEY_ADDRESS,DBHelper.KEY_CITY,DBHelper.KEY_GPSLONG,DBHelper.KEY_GPSLAT,DBHelper.KEY_IMAGEFILENAME  + ""};
int[] to = new int[] {R.id.businessname,R.id.address,R.id.city,R.id.gpslong,R.id.gpslat,R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);

R.id.imagefilenameButtonImageです。

ここに私のLogCatがあります:

01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed 

画像を表示するときに新しいエラーが発生する:

01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed

OutOfMemoryエラーを修正するには、次のようなことを行う必要があります。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);

このinSampleSizeオプションはメモリ消費を削減します。

ここには完全な方法があります。 まず、コンテンツ自体をデコードせずに画像サイズを読み込みます。 次に、最高のinSampleSize値を見つけ、2のべき乗でなければならず、最後に画像がデコードされます。

// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);

        // The new size we want to scale to
        final int REQUIRED_SIZE=70;

        // Find the correct scale value. It should be the power of 2.
        int scale = 1;
        while(o.outWidth / scale / 2 >= REQUIRED_SIZE && 
              o.outHeight / scale / 2 >= REQUIRED_SIZE) {
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {}
    return null;
}

Android Trainingクラスの「 ビットマップを効率的に表示する 」では、 java.lang.OutOfMemoryError: bitmap size exceeds VM budget例外を理解して対処するための素晴らしい情報を提供していjava.lang.OutOfMemoryError: bitmap size exceeds VM budget読み込むときに、 java.lang.OutOfMemoryError: bitmap size exceeds VM budget

ビットマップの寸法とタイプを読み込む

BitmapFactoryクラスは、さまざまなソースからBitmapを作成するためのいくつかのデコードメソッド( decodeByteArray()decodeFile()decodeResource()など)を提供します。 画像データソースに基づいて最適なデコード方法を選択してください。 これらのメソッドは、構築されたビットマップにメモリをOutOfMemoryうとするため、 OutOfMemory例外が発生しやすくなります。 各タイプのデコードメソッドにはBitmapFactory.Optionsクラスを使用してデコードオプションを指定できる追加のシグネチャがあります。 デコード中にinJustDecodeBoundsプロパティをtrue設定すると、メモリ割り当てが回避され、ビットマップオブジェクトにnullが返されnullが、 outWidthoutHeightおよびoutMimeTypeが設定されます。 このテクニックを使用すると、ビットマップの構築(およびメモリ割り当て)の前にイメージデータのサイズとタイプを読み取ることができます。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

java.lang.OutOfMemory例外を回避するには、使用可能なメモリ内に適切に収まる大きさの予測可能なイメージデータをソースから絶対に信頼するまで、ビットマップのサイズをチェックしてからデコードしてください。

スケールダウンされたバージョンをメモリにロードする

画像の寸法がわかったので、それらを使用して、フル画像をメモリにロードするか、サブサンプリングされたバージョンをロードするかを決定することができます。 考慮すべきいくつかの要因は次のとおりです。

  • メモリ内の全画像を読み込む際の推定メモリ使用量。
  • アプリケーションの他のメモリ要件がある場合に、このイメージのロードにコミットするメモリ量。
  • イメージがロードされるターゲットImageViewまたはUIコンポーネントの寸法。
  • 現在のデバイスの画面サイズと密度。

たとえば、最終的にImageView 128x96ピクセルのサムネイルで表示される場合、1024x768ピクセルイメージをメモリにロードする価値はありません。

デコーダに画像のサブサンプリングを指示し、より小さなバージョンをメモリにロードするには、 BitmapFactory.OptionsオブジェクトでinSampleSizetrueに設定します。 例えば、4のinSampleSizeでデコードされる解像度2048x1536の画像は、約512x384のビットマップを生成する。 これをメモリにロードすると、フルイメージ( ARGB_8888ビットマップ構成を前提とします)では12MBではなく0.75MBが使用されます。 ターゲットの幅と高さに基づいて2の累乗であるサンプルサイズの値を計算する方法は次のとおりです。

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

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

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

inSampleSizeドキュメントのように、デコーダが最終値を2の最も近い累乗に丸めて使用するため、2つの値の累乗が計算されます。

このメソッドを使用するには、最初にinJustDecodeBoundstrueに設定してデコードし、オプションを渡してから新しいinSampleSize値とinJustDecodeBoundsfalse設定して再度デコードしfalse

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

このメソッドを使用すると、100x100ピクセルのサムネイルを表示するImageViewに任意のサイズのビットマップを簡単にロードできます(次のコード例を参照)。

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

必要に応じて適切なBitmapFactory.decode*メソッドを置き換えることによって、同様のプロセスに従って、他のソースからビットマップをデコードすることができます。


ここに2つの問題があります....

  • ビットマップメモリ​​はVMヒープにはなく、ネイティブヒープにあります。BitmapFactoryを参照してください。
  • ネイティブヒープのガベージコレクションはVMヒープよりもレイジーです。アクティビティのonPauseまたはonDestroyを実行するたびに、bitmap.recycleとbitmap = nullを実行することについてかなり積極的にする必要があります

このコードはdrawableから大きなビットマップを読み込むのに役立ちます

public class BitmapUtilsTask extends AsyncTask<Object, Void, Bitmap> {

    Context context;

    public BitmapUtilsTask(Context context) {
        this.context = context;
    }

    /**
     * Loads a bitmap from the specified url.
     * 
     * @param url The location of the bitmap asset
     * @return The bitmap, or null if it could not be loaded
     * @throws IOException
     * @throws MalformedURLException
     */
    public Bitmap getBitmap() throws MalformedURLException, IOException {       

        // Get the source image's dimensions
        int desiredWidth = 1000;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        int srcWidth = options.outWidth;
        int srcHeight = options.outHeight;

        // Only scale if the source is big enough. This code is just trying
        // to fit a image into a certain width.
        if (desiredWidth > srcWidth)
            desiredWidth = srcWidth;

        // Calculate the correct inSampleSize/scale value. This helps reduce
        // memory use. It should be a power of 2
        int inSampleSize = 1;
        while (srcWidth / 2 > desiredWidth) {
            srcWidth /= 2;
            srcHeight /= 2;
            inSampleSize *= 2;
        }
        // Decode with inSampleSize
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = inSampleSize;
        options.inScaled = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        options.inPurgeable = true;
        Bitmap sampledSrcBitmap;

        sampledSrcBitmap =  BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        return sampledSrcBitmap;
    }

    /**
     * The system calls this to perform work in a worker thread and delivers
     * it the parameters given to AsyncTask.execute()
     */
    @Override
    protected Bitmap doInBackground(Object... item) {
        try { 
          return getBitmap();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

これは、画像をコミュニティに読み込んで処理するための私のユーティリティクラスを共有する適切な場所のようです。あなたはそれを自由に使用して変更することができます。

package com.emil;

import java.io.IOException;
import java.io.InputStream;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

/**
 * A class to load and process images of various sizes from input streams and file paths.
 * 
 * @author Emil http://.com/users/220710/emil
 *
 */
public class ImageProcessing {

    public static Bitmap getBitmap(InputStream stream, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Bitmap getBitmap(String imgPath, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    public static Dimensions getDimensions(InputStream stream) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Dimensions getDimensions(String imgPath) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    private static boolean checkDecode(BitmapFactory.Options options){
        // Did decode work?
        if( options.outWidth<0 || options.outHeight<0 ){
            return false;
        }else{
            return true;
        }
    }

    /**
     * Creates a Bitmap that is of the minimum dimensions necessary
     * @param bm
     * @param min
     * @return
     */
    public static Bitmap createMinimalBitmap(Bitmap bm, ImageProcessing.Minimize min){
        int newWidth, newHeight;
        switch(min.type){
        case WIDTH:
            if(bm.getWidth()>min.minWidth){
                newWidth=min.minWidth;
                newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case HEIGHT:
            if(bm.getHeight()>min.minHeight){
                newHeight=min.minHeight;
                newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case BOTH: // minimize to the maximum dimension
        case MAX:
            if(bm.getHeight()>bm.getWidth()){
                // Height needs to minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minHeight;
                if(bm.getHeight()>min.minDim){
                    newHeight=min.minDim;
                    newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }else{
                // Width needs to be minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minWidth;
                if(bm.getWidth()>min.minDim){
                    newWidth=min.minDim;
                    newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }
            break;
        default:
            // No resize
            newWidth=bm.getWidth();
            newHeight=bm.getHeight();
        }
        return Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
    }

    public static int getScaledWidth(int height, Bitmap bm){
        return (int)(((double)bm.getWidth()/bm.getHeight())*height);
    }

    public static int getScaledHeight(int width, Bitmap bm){
        return (int)(((double)bm.getHeight()/bm.getWidth())*width);
    }

    /**
     * Get the proper sample size to meet minimization restraints
     * @param dim
     * @param min
     * @param multipleOf2 for fastest processing it is recommended that the sample size be a multiple of 2
     * @return
     */
    public static int getSampleSize(ImageProcessing.Dimensions dim, ImageProcessing.Minimize min, boolean multipleOf2){
        switch(min.type){
        case WIDTH:
            return ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
        case HEIGHT:
            return ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
        case BOTH:
            int widthMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
            int heightMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
            // Return the smaller of the two
            if(widthMaxSampleSize<heightMaxSampleSize){
                return widthMaxSampleSize;
            }else{
                return heightMaxSampleSize;
            }
        case MAX:
            // Find the larger dimension and go bases on that
            if(dim.width>dim.height){
                return ImageProcessing.getMaxSampleSize(dim.width, min.minDim, multipleOf2);
            }else{
                return ImageProcessing.getMaxSampleSize(dim.height, min.minDim, multipleOf2);
            }
        }
        return 1;
    }

    public static int getMaxSampleSize(int dim, int min, boolean multipleOf2){
        int add=multipleOf2 ? 2 : 1;
        int size=0;
        while(min<(dim/(size+add))){
            size+=add;
        }
        size = size==0 ? 1 : size;
        return size;        
    }

    public static class Dimensions {
        int width;
        int height;

        public Dimensions(int width, int height) {
            super();
            this.width = width;
            this.height = height;
        }

        @Override
        public String toString() {
            return width+" x "+height;
        }
    }

    public static class Minimize {
        public enum Type {
            WIDTH,HEIGHT,BOTH,MAX
        }
        Integer minWidth;
        Integer minHeight;
        Integer minDim;
        Type type;

        public Minimize(int min, Type type) {
            super();
            this.type = type;
            switch(type){
            case WIDTH:
                this.minWidth=min;
                break;
            case HEIGHT:
                this.minHeight=min;
                break;
            case BOTH:
                this.minWidth=min;
                this.minHeight=min;
                break;
            case MAX:
                this.minDim=min;
                break;
            }
        }

        public Minimize(int minWidth, int minHeight) {
            super();
            this.type=Type.BOTH;
            this.minWidth = minWidth;
            this.minHeight = minHeight;
        }

    }

    /**
     * Estimates size of Bitmap in bytes depending on dimensions and Bitmap.Config
     * @param width
     * @param height
     * @param config
     * @return
     */
    public static long estimateBitmapBytes(int width, int height, Bitmap.Config config){
        long pixels=width*height;
        switch(config){
        case ALPHA_8: // 1 byte per pixel
            return pixels;
        case ARGB_4444: // 2 bytes per pixel, but depreciated
            return pixels*2;
        case ARGB_8888: // 4 bytes per pixel
            return pixels*4;
        case RGB_565: // 2 bytes per pixel
            return pixels*2;
        default:
            return pixels;
        }
    }

    private static BitmapFactory.Options getOptionsForDimensions(){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds=true;
        return options;
    }

    private static BitmapFactory.Options getOptionsForSampling(int sampleSize, Bitmap.Config bitmapConfig){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = sampleSize;
        options.inScaled = false;
        options.inPreferredConfig = bitmapConfig;
        return options;
    }
}

これは私のために働いた!

public Bitmap readAssetsBitmap(String filename) throws IOException {
    try {
        BitmapFactory.Options options = new BitmapFactory.Options(); 
        options.inPurgeable = true;
        Bitmap bitmap = BitmapFactory.decodeStream(assets.open(filename), null, options);
        if(bitmap == null) {
            throw new IOException("File cannot be opened: It's value is null");
        } else {
            return bitmap;
        }
    } catch (IOException e) {
        throw new IOException("File cannot be opened: " + e.getMessage());
    }
}

これらのコードをSdCardから選択したすべてのイメージに使用するか、ビットマップオブジェクトを変換するために描画可能にします。

Resources res = getResources();
WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = window.getDefaultDisplay();
@SuppressWarnings("deprecation")
int width = display.getWidth();
@SuppressWarnings("deprecation")
int height = display.getHeight();
try {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    bitmap = Bitmap.createScaledBitmap(BitmapFactory
        .decodeFile(ImageData_Path.get(img_pos).getPath()),
        width, height, true);
} catch (OutOfMemoryError e) {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Config.RGB_565;
    options.inSampleSize = 1;
    options.inPurgeable = true;
    bitmapBitmap.createScaledBitmap(BitmapFactory.decodeFile(ImageData_Path.get(img_pos)
        .getPath().toString(), options), width, height,true);
}
return bitmap;

ImageData_Path.get(img_pos).getPath()のイメージパスを使用してください。


私の2セント:ビットマップでOOMエラーを解決しました:

a)画像を2倍にスケーリングする

b)リストビュー用のカスタムアダプタでPicassoライブラリを使用し、getViewでワンコールします。Picasso.with(context).load(R.id.myImage).into(R.id.myImageView);


私はFedorのコードを少し改善しました。 それは基本的に同じですが、(私の意見では)醜いwhileループはなく、常に2の威力を発揮します。 元の解決策を作るためにFedorに誇りを持って、私は彼が見つかるまで立ち往生していました。そして、私はこれを作ることができました:)

 private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}

私はiOSの経験から来て、イメージを読み込んで表示するという基本的な問題を発見することに不満を感じました。 結局のところ、この問題を抱えているすべての人は、合理的なサイズの画像を表示しようとしています。 とにかく、私の問題を解決した2つの変更があります(私のアプリは非常に反応しました)。

1) BitmapFactory.decodeXYZ()を実行するたびに、 BitmapFactory.OptionsinPurgeable設定してBitmapFactory.OptionsinPurgeableようにしtrue (できればinInputShareabletrue設定することをおinInputShareabletrue )。

2) Bitmap.createBitmap(width, height, Config.ARGB_8888)使用しBitmap.createBitmap(width, height, Config.ARGB_8888) 。 私は決して意味しない! 私はいくつかのパスの後にメモリエラーを発生させないことを決して持っていない。 recycle()System.gc() 、どんなに助けてもSystem.gc()ません。 常に例外が発生しました。 実際に動作するもう1つの方法は、ドロアブル(または上記の手順1でデコードした別のビットマップ)にダミーイメージを作成し、それを任意のサイズに再スケーリングし、結果のビットマップを操作します(キャンバスに渡すなどもっと楽しいために)。 したがって、代わりに使用する必要があるのはBitmap.createScaledBitmap(srcBitmap, width, height, false)です。 何らかの理由でbrute force createメソッドを使用しなければならない場合は、少なくともConfig.ARGB_4444ます。

これは、ほとんどの場合、日数ではなく時間を節約することが保証されています。 あなたが間違ったサイズや画像を劣化させることを考慮しないかぎり、画像のスケーリングなどについては、実際には機能しません。


私は最近、OOMの例外とキャッシングに関する多くの質問を見てきました。 開発者ガイドにはこれに関する記事がありますが、適切な方法で実装すると失敗する傾向があります。

このため、私はAndroid環境でのキャッシュを示すサンプルアプリケーションを作成しました。 この実装はまだOOMを取得していません。

この答えの最後に、ソースコードへのリンクがあります。

要件:

  • Android API 2.1以上(API 1.6のアプリケーションで使用できるメモリを取得できませんでした。これはAPI 1.6では動作しません)
  • Androidサポートパッケージ

特徴:

  • オリエンテーションが変更された場合はシングルトンを使用してキャッシュを保持します。
  • 割り当てられたアプリケーションメモリの8分1をキャッシュに使用する(必要に応じて変更する)
  • 大きなビットマップは拡大縮小されます (許可する最大ピクセルを定義できます)
  • ビットマップをダウンロードする前にインターネット接続が利用できるようするコントロール
  • 行ごとに1 つのタスクしかインスタンス化していないことを確認します
  • ListView離れている場合 、単にビットマップをダウンロードしません

これには以下が含まれます:

  • ディスクキャッシュ。 これはとにかく簡単に実装できるはずです。ディスクからビットマップを取得する別のタスクを指すだけです

サンプルコード:

ダウンロードされている画像は、Flickrの画像(75x75)です。 ただし、処理する画像URLを指定してください。最大値を超えると、アプリケーションはそれを縮小します。 このアプリケーションでは、URLはString配列内にあります。

LruCacheは、ビットマップを扱う良い方法があります。 しかし、このアプリケーションでは、アプリケーションをより実現可能にするために作成した別のキャッシュクラスの中にLruCacheインスタンスを配置しました。

Cache.javaの重要なもの( loadBitmap()メソッドが最も重要です):

public Cache(int size, int maxWidth, int maxHeight) {
    // Into the constructor you add the maximum pixels
    // that you want to allow in order to not scale images.
    mMaxWidth = maxWidth;
    mMaxHeight = maxHeight;

    mBitmapCache = new LruCache<String, Bitmap>(size) {
        protected int sizeOf(String key, Bitmap b) {
            // Assuming that one pixel contains four bytes.
            return b.getHeight() * b.getWidth() * 4;
        }
    };

    mCurrentTasks = new ArrayList<String>();    
}

/**
 * Gets a bitmap from cache. 
 * If it is not in cache, this method will:
 * 
 * 1: check if the bitmap url is currently being processed in the
 * BitmapLoaderTask and cancel if it is already in a task (a control to see
 * if it's inside the currentTasks list).
 * 
 * 2: check if an internet connection is available and continue if so.
 * 
 * 3: download the bitmap, scale the bitmap if necessary and put it into
 * the memory cache.
 * 
 * 4: Remove the bitmap url from the currentTasks list.
 * 
 * 5: Notify the ListAdapter.
 * 
 * @param mainActivity - Reference to activity object, in order to
 * call notifyDataSetChanged() on the ListAdapter.
 * @param imageKey - The bitmap url (will be the key).
 * @param imageView - The ImageView that should get an
 * available bitmap or a placeholder image.
 * @param isScrolling - If set to true, we skip executing more tasks since
 * the user probably has flinged away the view.
 */
public void loadBitmap(MainActivity mainActivity, 
        String imageKey, ImageView imageView,
        boolean isScrolling) {
    final Bitmap bitmap = getBitmapFromCache(imageKey); 

    if (bitmap != null) {
        imageView.setImageBitmap(bitmap);
    } else {
        imageView.setImageResource(R.drawable.ic_launcher);
        if (!isScrolling && !mCurrentTasks.contains(imageKey) && 
                mainActivity.internetIsAvailable()) {
            BitmapLoaderTask task = new BitmapLoaderTask(imageKey,
                    mainActivity.getAdapter());
            task.execute();
        }
    } 
}

ディスクキャッシングを実装しない限り、Cache.javaファイル内の何かを編集する必要はありません。

MainActivity.javaの重要なこと:

public void onScrollStateChanged(AbsListView view, int scrollState) {
    if (view.getId() == android.R.id.list) {
        // Set scrolling to true only if the user has flinged the       
        // ListView away, hence we skip downloading a series
        // of unnecessary bitmaps that the user probably
        // just want to skip anyways. If we scroll slowly it
        // will still download bitmaps - that means
        // that the application won't wait for the user
        // to lift its finger off the screen in order to
        // download.
        if (scrollState == SCROLL_STATE_FLING) {
            mIsScrolling = true;
        } else {
            mIsScrolling = false;
            mListAdapter.notifyDataSetChanged();
        }
    } 
}

// Inside ListAdapter...
@Override
public View getView(final int position, View convertView, ViewGroup parent) {           
    View row = convertView;
    final ViewHolder holder;

    if (row == null) {
        LayoutInflater inflater = getLayoutInflater();
        row = inflater.inflate(R.layout.main_listview_row, parent, false);  
        holder = new ViewHolder(row);
        row.setTag(holder);
    } else {
        holder = (ViewHolder) row.getTag();
    }   

    final Row rowObject = getItem(position);

    // Look at the loadBitmap() method description...
    holder.mTextView.setText(rowObject.mText);      
    mCache.loadBitmap(MainActivity.this,
            rowObject.mBitmapUrl, holder.mImageView,
            mIsScrolling);  

    return row;
}

getView()は非常に頻繁に呼び出されます。 行ごとに無限のスレッドを開始しないようにするためのチェックを実装していない場合は、イメージをそこにダウンロードすることは通常はお勧めできません。 Cache.javaは、 rowObject.mBitmapUrlすでにタスク内にあるかどうかをチェックし、存在する場合は別のタスクを開始しません。 したがって、 AsyncTaskプールからの作業キューの制限を超えない可能性が最も高いです。

ダウンロード:

ソースコードはhttps://www.dropbox.com/s/pvr9zyl811tfeem/ListViewImageCache.zipからダウンロードできます。

最後の言葉:

私はこれを数週間テストしましたが、まだOOMの例外は1つもありません。 エミュレータ、Nexus One、Nexus Sでこれをテストしました。HD品質の画像を含む画像URLをテストしました。 唯一のボトルネックは、ダウンロードに時間がかかることです。

OOMが出現すると想像することができるシナリオは1つしかありません。それは、実際に大きな画像を多数ダウンロードし、スケーリングしてキャッシュに入れる前に、同時に多くのメモリを使用しOOMを発生させることです。 しかし、それはとにかく理想的な状況でもなく、より実現可能な方法で解決することはできないでしょう。

コメントのエラーを報告してください! :-)


私は次のようにして同じ問題を解決しました。

Bitmap b = null;
Drawable d;
ImageView i = new ImageView(mContext);
try {
    b = Bitmap.createBitmap(320,424,Bitmap.Config.RGB_565);
    b.eraseColor(0xFFFFFFFF);
    Rect r = new Rect(0, 0,320 , 424);
    Canvas c = new Canvas(b);
    Paint p = new Paint();
    p.setColor(0xFFC0C0C0);
    c.drawRect(r, p);
    d = mContext.getResources().getDrawable(mImageIds[position]);
    d.setBounds(r);
    d.draw(c);

    /*   
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inTempStorage = new byte[128*1024];
        b = BitmapFactory.decodeStream(mContext.getResources().openRawResource(mImageIds[position]), null, o2);
        o2.inSampleSize=16;
        o2.inPurgeable = true;
    */
} catch (Exception e) {

}
i.setImageBitmap(b);

bitmap.recycle();これを使用すると、画質の問題がなくなります。


ここで大きな答えがありましたが、私はこの問題に対処するために完全に使えるクラスを求めていました。

ここで私のBitmapHelperクラスはOutOfMemoryErrorの証明です:-)

import java.io.File;
import java.io.FileInputStream;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

public class BitmapHelper
{

    //decodes image and scales it to reduce memory consumption
    public static Bitmap decodeFile(File bitmapFile, int requiredWidth, int requiredHeight, boolean quickAndDirty)
    {
        try
        {
            //Decode image size
            BitmapFactory.Options bitmapSizeOptions = new BitmapFactory.Options();
            bitmapSizeOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapSizeOptions);

            // load image using inSampleSize adapted to required image size
            BitmapFactory.Options bitmapDecodeOptions = new BitmapFactory.Options();
            bitmapDecodeOptions.inTempStorage = new byte[16 * 1024];
            bitmapDecodeOptions.inSampleSize = computeInSampleSize(bitmapSizeOptions, requiredWidth, requiredHeight, false);
            bitmapDecodeOptions.inPurgeable = true;
            bitmapDecodeOptions.inDither = !quickAndDirty;
            bitmapDecodeOptions.inPreferredConfig = quickAndDirty ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;

            Bitmap decodedBitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapDecodeOptions);

            // scale bitmap to mathc required size (and keep aspect ratio)

            float srcWidth = (float) bitmapDecodeOptions.outWidth;
            float srcHeight = (float) bitmapDecodeOptions.outHeight;

            float dstWidth = (float) requiredWidth;
            float dstHeight = (float) requiredHeight;

            float srcAspectRatio = srcWidth / srcHeight;
            float dstAspectRatio = dstWidth / dstHeight;

            // recycleDecodedBitmap is used to know if we must recycle intermediary 'decodedBitmap'
            // (DO NOT recycle it right away: wait for end of bitmap manipulation process to avoid
            // java.lang.RuntimeException: Canvas: trying to use a recycled bitmap [email protected]
            // I do not excatly understand why, but this way it's OK

            boolean recycleDecodedBitmap = false;

            Bitmap scaledBitmap = decodedBitmap;
            if (srcAspectRatio < dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) dstWidth, (int) (srcHeight * (dstWidth / srcWidth)));
                // will recycle recycleDecodedBitmap
                recycleDecodedBitmap = true;
            }
            else if (srcAspectRatio > dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) (srcWidth * (dstHeight / srcHeight)), (int) dstHeight);
                recycleDecodedBitmap = true;
            }

            // crop image to match required image size

            int scaledBitmapWidth = scaledBitmap.getWidth();
            int scaledBitmapHeight = scaledBitmap.getHeight();

            Bitmap croppedBitmap = scaledBitmap;

            if (scaledBitmapWidth > requiredWidth)
            {
                int xOffset = (scaledBitmapWidth - requiredWidth) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, xOffset, 0, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }
            else if (scaledBitmapHeight > requiredHeight)
            {
                int yOffset = (scaledBitmapHeight - requiredHeight) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, 0, yOffset, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }

            if (recycleDecodedBitmap)
            {
                decodedBitmap.recycle();
            }
            decodedBitmap = null;

            scaledBitmap = null;
            return croppedBitmap;
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * compute powerOf2 or exact scale to be used as {@link BitmapFactory.Options#inSampleSize} value (for subSampling)
     * 
     * @param requiredWidth
     * @param requiredHeight
     * @param powerOf2
     *            weither we want a power of 2 sclae or not
     * @return
     */
    public static int computeInSampleSize(BitmapFactory.Options options, int dstWidth, int dstHeight, boolean powerOf2)
    {
        int inSampleSize = 1;

        // Raw height and width of image
        final int srcHeight = options.outHeight;
        final int srcWidth = options.outWidth;

        if (powerOf2)
        {
            //Find the correct scale value. It should be the power of 2.

            int tmpWidth = srcWidth, tmpHeight = srcHeight;
            while (true)
            {
                if (tmpWidth / 2 < dstWidth || tmpHeight / 2 < dstHeight)
                    break;
                tmpWidth /= 2;
                tmpHeight /= 2;
                inSampleSize *= 2;
            }
        }
        else
        {
            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) srcHeight / (float) dstHeight);
            final int widthRatio = Math.round((float) srcWidth / (float) dstWidth);

            // 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;
    }

    public static Bitmap drawableToBitmap(Drawable drawable)
    {
        if (drawable instanceof BitmapDrawable)
        {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }

    public static Bitmap getScaledBitmap(Bitmap bitmap, int newWidth, int newHeight)
    {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;

        // CREATE A MATRIX FOR THE MANIPULATION
        Matrix matrix = new Matrix();
        // RESIZE THE BIT MAP
        matrix.postScale(scaleWidth, scaleHeight);

        // RECREATE THE NEW BITMAP
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
        return resizedBitmap;
    }

}

この問題は、Androidエミュレータでのみ発生します。私もエミュレータでこの問題に直面しましたが、デバイスをチェックインしたところ正常に動作しました。

だから、デバイスをチェックインしてください。それはデバイスで実行される可能性があります。


これは非常に長い実行中の問題で、多くの異なる説明があるようです。私はこの2つの最も一般的な答えのアドバイスを受けましたが、これらのどちらも、VMの私の問題を解決して、プロセスのデコード部分を実行するバイトを用意できないと主張しました。いくつかの掘り下げの後で、ここでの本当の問題は、NATIVEヒープから離れていくデコードプロセスだということを学びました。

ここを参照してください:BitmapFactory OOMは私にナットを駆動

それは別のディスカッションスレッドに私を導き、そこでこの問題の解決策をいくつか見つけました。1つはSystem.gc();、画像が表示された後に手動で呼び出すことです。しかし、実際には、ネイティブヒープを減らすために、アプリでメモリを増やしています。2.0(Donut)のリリースより優れた解決策は、BitmapFactoryオプション "inPurgeable"を使用することです。だから私はo2.inPurgeable=true;ちょうどo2.inSampleSize=scale;

そのトピックの詳細はこちら:メモリヒープの上限は6Mですか?

さて、これのすべてを言って、私はJavaとAndroidの完全なダンスです。だから、もしこれがこの問題を解決するためのひどい方法だと思うなら、おそらく正しいでしょう。;-)しかし、これは私のために不思議に思ったことがあり、VMをヒープキャッシュから実行することが不可能になっています。私が見つけることができる唯一の欠点は、キャッシュされた描画されたイメージを捨てていることです。つまり、そのイメージに戻ってきたら、毎回それを再描画しています。私のアプリケーションの仕組みの場合、それは実際問題ではありません。あなたのマイレージは異なる場合があります。


一般のAndroidデバイスのヒープサイズが唯一の16メガバイトである(デバイス/ OS参照ポストから変わるヒープサイズあなたが画像をロードすると、それは16メガバイトのサイズを横切る場合)、それは代わりに、ロード用のビットマップを使用しての、メモリ不足の例外がスローされますSDカードやリソースから、またはネットワークからでもgetImageUriを使用しようとすると、ビットマップを読み込むにはさらに多くのメモリが必要になります。


上記の答えは私のために働いたことはありませんでしたが、私は問題を解決する恐ろしい醜い回避策を考え出しました。私はリソースとしてプロジェクトに1x1ピクセルの非常に小さいイメージを追加し、それをガベージコレクションに呼び出す前にImageViewにロードしました。ImageViewがBitmapをリリースしていないかもしれないと思うので、GCは決してそれをピックアップしませんでした。それは醜いですが、今のところ働いているようです。

if (bitmap != null)
{
  bitmap.recycle();
  bitmap = null;
}
if (imageView != null)
{
  imageView.setImageResource(R.drawable.tiny); // This is my 1x1 png.
}
System.gc();

imageView.setImageBitmap(...); // Do whatever you need to do to load the image you want.

私は、あらゆる種類のスケーリングを必要としない、より効果的なソリューションを持っています。単純にビットマップを1回だけデコードしてから、名前にマップしてキャッシュします。次に、名前に対してビットマップを取得し、ImageViewで設定します。何もする必要はありません。

これは、デコードされたビットマップの実際のバイナリデータがdalvik VMヒープ内に格納されないために機能します。それは外部に保存されます。そのため、ビットマップをデコードするたびに、GCヒープの外側にメモリを割り当てます。このメモリはGCによって再生されません

あなたがこれを高く評価するのを助けるために、あなたが引き出し可能なフォルダにウルイメージを保持していると想像してください。getResources()。getDrwable(R.drawable。)を実行するだけでイメージを取得できます。これは毎回あなたのイメージをデコードするのではなく、あなたがそれを呼び出すたびに既にデコードされたインスタンスを再利用します。したがって本質的にキャッシュされます。

あなたのイメージはどこかのファイルにあるので(または外部のサーバから来ることさえあるかもしれません)、デコードされたビットマップインスタンスをキャッシュして、必要な場所で再利用することはあなたの責任です。

お役に立てれば。


私はこれらのソリューションをテストして一日を過ごしましたが、私のために働いたのは、画像を取得して手動でGCを呼び出すための上記のアプローチだけです。私が知っているGCは手動でコールする必要はありませんが、私は重い負荷テストの下で私のアプリケーションを置くときに活動の間に切り替えます。私のアプリはリストビュー内のサムネイル画像のリストを持っています(アクティビティAを言うことができます)。その画像の1つをクリックすると、そのアイテムのメイン画像を表示する別のアクティビティ(アクティビティBを言うことができます)に移動します。私は2つのアクティビティの間を行き来すると、最終的にOOMエラーが発生し、アプリケーションが強制終了します。

私がリストビューを半分下ろすとクラッシュするでしょう。

今私がアクティビティBで次のことを実装すると、リストビュー全体を何の問題もなく進んでいくことができます。

@Override
public void onDestroy()
{   
    Cleanup();
    super.onDestroy();
}

private void Cleanup()
{    
    bitmap.recycle();
    System.gc();
    Runtime.getRuntime().gc();  
}




android-bitmap