ВМ не хватает памяти при получении изображений из кэша - PullRequest
1 голос
/ 07 июля 2011

Известно, что проблема, с которой я сталкиваюсь, связана с устройствами с 16 МБ динамической памяти, и мне нужно ее решить. У меня есть потоки, получающие изображения с сервера (большие изображения) и их кэширование, а затем отображение. Проблема в том, что мне не хватает памяти. Чтобы объяснить немного дальше, я выкладываю свой журнал cat, а также некоторый код:

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)

Любая помощь будет принята с благодарностью, Спасибо за чтение.

Ответы [ 2 ]

2 голосов
/ 07 июля 2011

Вам необходимо повторно сэмплировать и масштабировать ваши растровые изображения до , чтобы вставить их в кэш внутри вашего загрузчика изображений.

В прошлом мне приходилось определять размер файла изображения до его загрузки из Интернета. Если он слишком большой, я просто использую изображение по умолчанию.

Кроме того, я также снизил разрешение сохраненного изображения, настроив размер выборки 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);
    }

}

{...}

До сих пор это работало для меня, и проверка размеров файлов INSANE спасла приложение от сбоев во многих случаях, не говоря уже о том, что оно может просто пропустить загрузку массивного файла в фоновом режиме.

1 голос
/ 07 июля 2011
...