从缓存获取图像时虚拟机内存不足

发布于 2024-11-18 14:19:32 字数 5908 浏览 2 评论 0原文

已知我正在解决的问题出现在具有 16 MB 堆内存的设备中,我需要解决它。我有线程从服务器获取图像(大图像)并将它们缓存然后显示。问题是我的内存不足。为了进一步解释一下,我发布了我的日志猫以及一些代码:

public class Test extends Activity implements OnClickListener {
ImageView paint, wheels, shadow;

Button b;
int j = 2;
int i = 1;
String pathToWheels, pathToPaint, pathToShadow;
String stringAngle;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    MemoryInfo dbm = new MemoryInfo();
    Debug.getMemoryInfo(dbm);
    b = (Button) findViewById(R.id.button1);
    b.setOnClickListener(this);



    wheels = (ImageView) findViewById(R.id.imageView3);
    paint = (ImageView) findViewById(R.id.imageView2);
    shadow = (ImageView) findViewById(R.id.imageView1);



    while (i < 13) {

        stringAngle = Integer.toString(i);
        String pathToWheels = url;
        String pathToPaint = url;
        String pathToShadow = url;

        if (Cache.getCacheFile(pathToWheels) == null) {
            ImageDownloader downloader1 = new ImageDownloader(wheels);
            downloader1
                    .execute(url);
        } else if (Cache.getCacheFile(pathToShadow) == null) {
            ImageDownloader downloader2 = new ImageDownloader(shadow);
            downloader2
                    .execute(url);


        } else if (Cache.getCacheFile(pathToPaint) == null) {

            ImageDownloader downloader = new ImageDownloader(paint);
            downloader
                    .execute(url);


        }

        i++;


    }

}



@Override
protected void onPause() {
    super.onPause();
    Toast.makeText(getApplicationContext(),"Pause",
            Toast.LENGTH_SHORT).show();

}

@Override
public void onClick(View v) {


    if (v.getId() == R.id.button1) {
        if (j == 13) 
        {
            j = 1;  
        }
        String s = Integer.toString(j);
        wheels.setImageBitmap(Cache
                .getCacheFile(url));
        paint.setImageBitmap(Cache
                .getCacheFile(url));
    shadow.setImageBitmap(Cache
                .getCacheFile(url));
        j++;



    }
}

}

从我的缓存类:

public static void saveCacheFile(String cacheUri, Bitmap image) {
    if(!isCacheWritable()) return;
    Log.i("CACHE S",cacheUri);
    cache.saveCacheFile(cacheUri, image);

从我的 CacheStore 类:

public Bitmap getCacheFile(String cacheUri) {
    if(bitmapMap.containsKey(cacheUri)) return (Bitmap)bitmapMap.get(cacheUri);

    if(!cacheMap.containsKey(cacheUri)) return null;
    String fileLocalName = cacheMap.get(cacheUri);
    File fullCacheDir = new     File(Environment.getExternalStorageDirectory().toString(),cacheDir);
    File fileUri = new File(fullCacheDir.toString(), fileLocalName);
    if(!fileUri.exists()) return null;
    Log.i("CACHE", "File "+cacheUri+" has been found in the Cache");
    Bitmap bm = BitmapFactory.decodeFile(fileUri.toString());
    Bitmap.createScaledBitmap(bm, 480, 320, true);
    bitmapMap.put(cacheUri, bm);
    return bm;
}

应用程序在这一行崩溃: Bitmap bm = BitmapFactory.decodeFile(fileUri.toString()); 我的内存不足了。

我的 logcat:

    07-06 23:40:20.720: ERROR/dalvikvm-heap(1844): 691200-byte external allocation too large for this process.
07-06 23:40:20.720: ERROR/GraphicsJNI(1844): VM won't let us allocate 691200 bytes
07-06 23:40:20.770: DEBUG/skia(1844): --- decoder->decode returned false
07-06 23:40:20.790: DEBUG/AndroidRuntime(1844): Shutting down VM
07-06 23:40:20.830: WARN/dalvikvm(1844): threadid=1: thread exiting with uncaught exception (group=0x4001d800)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844): FATAL EXCEPTION: main
07-06 23:40:20.890: ERROR/AndroidRuntime(1844): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:271)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:296)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at com.cache.CacheStore.getCacheFile(CacheStore.java:117)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at com.cache.Cache.getCacheFile(Cache.java:38)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at com.cache.Test.onClick(Test.java:118)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.view.View.performClick(View.java:2408)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.view.View$PerformClick.run(View.java:8816)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.os.Handler.handleCallback(Handler.java:587)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.os.Handler.dispatchMessage(Handler.java:92)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.os.Looper.loop(Looper.java:123)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.app.ActivityThread.main(ActivityThread.java:4627)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at java.lang.reflect.Method.invokeNative(Native Method)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at java.lang.reflect.Method.invoke(Method.java:521)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at dalvik.system.NativeStart.main(Native Method)

任何帮助将不胜感激,感谢您的阅读。

The problem that I'm addressing is known to be in devices with 16 mb of heap memory, and I need to solve it. I have threads getting images from a server (Big Images) and having them cached then displayed. The problem is that I run out of memory. To explain a little further I'm posting my log cat as well as some code:

public class Test extends Activity implements OnClickListener {
ImageView paint, wheels, shadow;

Button b;
int j = 2;
int i = 1;
String pathToWheels, pathToPaint, pathToShadow;
String stringAngle;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    MemoryInfo dbm = new MemoryInfo();
    Debug.getMemoryInfo(dbm);
    b = (Button) findViewById(R.id.button1);
    b.setOnClickListener(this);



    wheels = (ImageView) findViewById(R.id.imageView3);
    paint = (ImageView) findViewById(R.id.imageView2);
    shadow = (ImageView) findViewById(R.id.imageView1);



    while (i < 13) {

        stringAngle = Integer.toString(i);
        String pathToWheels = url;
        String pathToPaint = url;
        String pathToShadow = url;

        if (Cache.getCacheFile(pathToWheels) == null) {
            ImageDownloader downloader1 = new ImageDownloader(wheels);
            downloader1
                    .execute(url);
        } else if (Cache.getCacheFile(pathToShadow) == null) {
            ImageDownloader downloader2 = new ImageDownloader(shadow);
            downloader2
                    .execute(url);


        } else if (Cache.getCacheFile(pathToPaint) == null) {

            ImageDownloader downloader = new ImageDownloader(paint);
            downloader
                    .execute(url);


        }

        i++;


    }

}



@Override
protected void onPause() {
    super.onPause();
    Toast.makeText(getApplicationContext(),"Pause",
            Toast.LENGTH_SHORT).show();

}

@Override
public void onClick(View v) {


    if (v.getId() == R.id.button1) {
        if (j == 13) 
        {
            j = 1;  
        }
        String s = Integer.toString(j);
        wheels.setImageBitmap(Cache
                .getCacheFile(url));
        paint.setImageBitmap(Cache
                .getCacheFile(url));
    shadow.setImageBitmap(Cache
                .getCacheFile(url));
        j++;



    }
}

}

From my cache class:

public static void saveCacheFile(String cacheUri, Bitmap image) {
    if(!isCacheWritable()) return;
    Log.i("CACHE S",cacheUri);
    cache.saveCacheFile(cacheUri, image);

From my CacheStore class:

public Bitmap getCacheFile(String cacheUri) {
    if(bitmapMap.containsKey(cacheUri)) return (Bitmap)bitmapMap.get(cacheUri);

    if(!cacheMap.containsKey(cacheUri)) return null;
    String fileLocalName = cacheMap.get(cacheUri);
    File fullCacheDir = new     File(Environment.getExternalStorageDirectory().toString(),cacheDir);
    File fileUri = new File(fullCacheDir.toString(), fileLocalName);
    if(!fileUri.exists()) return null;
    Log.i("CACHE", "File "+cacheUri+" has been found in the Cache");
    Bitmap bm = BitmapFactory.decodeFile(fileUri.toString());
    Bitmap.createScaledBitmap(bm, 480, 320, true);
    bitmapMap.put(cacheUri, bm);
    return bm;
}

The app crashes at this line: Bitmap bm = BitmapFactory.decodeFile(fileUri.toString());
where I'm running out of memory.

My logcat:

    07-06 23:40:20.720: ERROR/dalvikvm-heap(1844): 691200-byte external allocation too large for this process.
07-06 23:40:20.720: ERROR/GraphicsJNI(1844): VM won't let us allocate 691200 bytes
07-06 23:40:20.770: DEBUG/skia(1844): --- decoder->decode returned false
07-06 23:40:20.790: DEBUG/AndroidRuntime(1844): Shutting down VM
07-06 23:40:20.830: WARN/dalvikvm(1844): threadid=1: thread exiting with uncaught exception (group=0x4001d800)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844): FATAL EXCEPTION: main
07-06 23:40:20.890: ERROR/AndroidRuntime(1844): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:459)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:271)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:296)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at com.cache.CacheStore.getCacheFile(CacheStore.java:117)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at com.cache.Cache.getCacheFile(Cache.java:38)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at com.cache.Test.onClick(Test.java:118)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.view.View.performClick(View.java:2408)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.view.View$PerformClick.run(View.java:8816)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.os.Handler.handleCallback(Handler.java:587)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.os.Handler.dispatchMessage(Handler.java:92)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.os.Looper.loop(Looper.java:123)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at android.app.ActivityThread.main(ActivityThread.java:4627)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at java.lang.reflect.Method.invokeNative(Native Method)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at java.lang.reflect.Method.invoke(Method.java:521)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
07-06 23:40:20.890: ERROR/AndroidRuntime(1844):     at dalvik.system.NativeStart.main(Native Method)

Any help would be greatly appreciated, Thanks for reading.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

不美如何 2024-11-25 14:19:32

您需要在将位图插入图像下载器内的缓存之前重新采样并缩放位图

过去,我必须在从网络下载图像之前检测图像的文件大小。如果它太大我就使用默认图像。

此外,我还通过调整 BitmapFactory 采样大小来降低保存图像的分辨率,并调整应用程序中最终图像的大小。 (这为缓存节省了大量空间)。

请参阅如何了解文件的大小下载它?

//somewhere inside image downloader class...
{...}

BitmapFactory.Options options = new BitmapFactory.Options();

if(enableSampling)
    options.inSampleSize = 2;

returnBitmap = BitmapFactory.decodeStream(is, null, options);   

if(returnBitmap != null)
{

    if(scaleImage)
        returnBitmap = Bitmap.createScaledBitmap(returnBitmap, width, height, true);

    // insert the image into the cache
    imageCache.put(url, new SoftReference<Bitmap>(returnBitmap));


    // Check the ImageView for nulls
    if(imageView != null && callback != null){
        ImageDisplayer displayer = new ImageDisplayer(imageView, returnBitmap, parentView, tag);
        callback.onImageReceived(displayer);
    }

}

{...}

到目前为止,这对我来说很有效,并且对疯狂文件大小的检查使应用程序免于多次崩溃,更不用说它可以跳过在后台下载大量文件。

You need to re-sample and scale your bitmaps prior to inserting them into the cache, inside your imagedownloader.

In the past I've had to detect the file size of the image prior to downloading it from the web. If it's too large I just use a default image.

Additionally, I also lowered the resolution of the saved image by adjusting the BitmapFactory Sampling size, and adjusted the size of the final image in your app. (this saved a lot of space for the cache).

See How to know the size of a file before downloading it?

//somewhere inside image downloader class...
{...}

BitmapFactory.Options options = new BitmapFactory.Options();

if(enableSampling)
    options.inSampleSize = 2;

returnBitmap = BitmapFactory.decodeStream(is, null, options);   

if(returnBitmap != null)
{

    if(scaleImage)
        returnBitmap = Bitmap.createScaledBitmap(returnBitmap, width, height, true);

    // insert the image into the cache
    imageCache.put(url, new SoftReference<Bitmap>(returnBitmap));


    // Check the ImageView for nulls
    if(imageView != null && callback != null){
        ImageDisplayer displayer = new ImageDisplayer(imageView, returnBitmap, parentView, tag);
        callback.onImageReceived(displayer);
    }

}

{...}

So far this has worked for me, and the check for INSANE file sizes has saved the app from crashing on numerous occasions, not to mention it can just skip downloaing a massive file in the background.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文