17
04-25 08:19:10.111 2431-2603/com.example.francesco.guidedautorewithtabs E/art﹕ Throwing OutOfMemoryError "Failed to allocate a 4194316 byte allocation with 1983651 free bytes and 1937KB until OOM" 
04-25 08:19:10.114 2431-2603/com.example.francesco.guidedautorewithtabs E/AndroidRuntime﹕ FATAL EXCEPTION: GLThread 285 
Process: com.example.francesco.guidedautorewithtabs, PID: 2431 
java.lang.OutOfMemoryError: Failed to allocate a 4194316 byte allocation with 1983651 free bytes and 1937KB until OOM 
     at dalvik.system.VMRuntime.newNonMovableArray(Native Method) 
     at android.graphics.Bitmap.nativeCreate(Native Method) 
     at android.graphics.Bitmap.createBitmap(Bitmap.java:817) 
     at android.graphics.Bitmap.createBitmap(Bitmap.java:794) 
     at android.graphics.Bitmap.createBitmap(Bitmap.java:761) 
     at com.google.maps.api.android.lib6.gmm6.o.c.l.a(Unknown Source) 
     at com.google.maps.api.android.lib6.gmm6.o.c.o.a(Unknown Source) 
     at com.google.maps.api.android.lib6.gmm6.o.c.o.a(Unknown Source) 
     at com.google.maps.api.android.lib6.gmm6.o.c.o.b(Unknown Source) 
     at com.google.maps.api.android.lib6.gmm6.o.c.b.ak.a(Unknown Source) 
     at com.google.maps.api.android.lib6.gmm6.o.c.b.as.a(Unknown Source) 
     at com.google.maps.api.android.lib6.gmm6.o.x.a(Unknown Source) 
     at com.google.maps.api.android.lib6.gmm6.o.l.a(Unknown Source) 
     at com.google.maps.api.android.lib6.gmm6.o.l.b(Unknown Source) 
     at com.google.maps.api.android.lib6.gmm6.o.cj.g(Unknown Source) 
     at com.google.maps.api.android.lib6.gmm6.o.cj.run(Unknown Source) 

В моем коде Android у меня есть много изображений, которые нужно отображать в разные фрагменты. Некоторые из них хранятся в папке с ресурсами, другие - из запроса http. По этой причине я реализую кэширование изображений, чтобы не идти против java.lang.OutOfMemoryError, но, тем не менее, эта ошибка сохраняется. Как я могу решить эту проблему? Ниже моей реализации кэширования изображений.Android java.lang.OutOfMemoryError?

ImageLoader

public class ImageLoader { 
MemoryCache memoryCache=new MemoryCache(); 
FileCache fileCache; 
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>()); 
ExecutorService executorService; 
private Context context; 

public ImageLoader(Context context){ 
    this.context=context; 
    fileCache=new FileCache(context); 
    executorService=Executors.newFixedThreadPool(5); 
} 

final int stub_id= R.drawable.no_image; 
public void DisplayImage(String url, ImageView imageView) 
{ 
    imageViews.put(imageView, url); 
    Bitmap bitmap=memoryCache.get(url); 
    if(bitmap!=null) 
     imageView.setImageBitmap(bitmap); 
    else 
    { 
     queuePhoto(url, imageView); 
     imageView.setImageResource(stub_id); 
    } 
} 

private void queuePhoto(String url, ImageView imageView) 
{ 
    PhotoToLoad p=new PhotoToLoad(url, imageView); 
    executorService.submit(new PhotosLoader(p)); 
} 

private Bitmap getBitmapFromAsset(String strName) 
{ 
    AssetManager assetManager = context.getAssets(); 
    InputStream istr = null; 
    try { 
     istr = assetManager.open(strName); 
    } catch (IOException e) { 
     e.printStackTrace(); 
    } 
    Bitmap bitmap = BitmapFactory.decodeStream(istr); 
    return bitmap; 
} 

private Bitmap getBitmap(String url) 
{ 
    File f=fileCache.getFile(url); 

    //from SD cache 
    Bitmap b = decodeFile(f); 
    if(b!=null) 
     return b; 

    //from assets 
    Bitmap bm = getBitmapFromAsset(url); 

    if(bm!=null) 
     return bm; 

    //from web 
    try { 
     Bitmap bitmap=null; 
     URL imageUrl = new URL(url); 
     HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection(); 
     conn.setConnectTimeout(30000); 
     conn.setReadTimeout(30000); 
     conn.setInstanceFollowRedirects(true); 
     InputStream is=conn.getInputStream(); 
     OutputStream os = new FileOutputStream(f); 
     Utils.CopyStream(is, os); 
     os.close(); 
     bitmap = decodeFile(f); 
     return bitmap; 
    } catch (Throwable ex){ 
     ex.printStackTrace(); 
     if(ex instanceof OutOfMemoryError) 
      memoryCache.clear(); 
     return null; 
    } 
} 

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

     //Find the correct scale value. It should be the power of 2. 
     final int REQUIRED_SIZE=70; 
     int width_tmp=o.outWidth, height_tmp=o.outHeight; 
     int scale=1; 
     while(true){ 
      if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE) 
       break; 
      width_tmp/=2; 
      height_tmp/=2; 
      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; 
} 

//Task for the queue 
private class PhotoToLoad 
{ 
    public String url; 
    public ImageView imageView; 
    public PhotoToLoad(String u, ImageView i){ 
     url=u; 
     imageView=i; 
    } 
} 

class PhotosLoader implements Runnable { 
    PhotoToLoad photoToLoad; 
    PhotosLoader(PhotoToLoad photoToLoad){ 
     this.photoToLoad=photoToLoad; 
    } 

    @Override 
    public void run() { 
     if(imageViewReused(photoToLoad)) 
      return; 
     Bitmap bmp=getBitmap(photoToLoad.url); 
     memoryCache.put(photoToLoad.url, bmp); 
     if(imageViewReused(photoToLoad)) 
      return; 
     BitmapDisplayer bd=new BitmapDisplayer(bmp, photoToLoad); 
     Activity a=(Activity)photoToLoad.imageView.getContext(); 
     a.runOnUiThread(bd); 
    } 
} 

boolean imageViewReused(PhotoToLoad photoToLoad){ 
    String tag=imageViews.get(photoToLoad.imageView); 
    if(tag==null || !tag.equals(photoToLoad.url)) 
     return true; 
    return false; 
} 

//Used to display bitmap in the UI thread 
class BitmapDisplayer implements Runnable 
{ 
    Bitmap bitmap; 
    PhotoToLoad photoToLoad; 
    public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;} 
    public void run() 
    { 
     if(imageViewReused(photoToLoad)) 
      return; 
     if(bitmap!=null) 
      photoToLoad.imageView.setImageBitmap(bitmap); 
     else 
      photoToLoad.imageView.setImageResource(stub_id); 
    } 
} 

public void clearCache() { 
    memoryCache.clear(); 
    fileCache.clear(); 
} 
} 

файловый кэш

public class FileCache { 

private File cacheDir; 

public FileCache(Context context){ 
    //Find the dir to save cached images 
    if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) 
     cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),"TTImages_cache"); 
    else 
     cacheDir=context.getCacheDir(); 
    if(!cacheDir.exists()) 
     cacheDir.mkdirs(); 
} 

public File getFile(String url){ 
    //I identify images by hashcode. Not a perfect solution, good for the demo. 
    String filename=String.valueOf(url.hashCode()); 
    //Another possible solution (thanks to grantland) 
    //String filename = URLEncoder.encode(url); 
    File f = new File(cacheDir, filename); 
    return f; 

} 

public void clear(){ 
    File[] files=cacheDir.listFiles(); 
    if(files==null) 
     return; 
    for(File f:files) 
     f.delete(); 
} 

} 

MemoryCache

public class MemoryCache { 

private static final String TAG = "MemoryCache"; 
private Map<String, Bitmap> cache=Collections.synchronizedMap(
     new LinkedHashMap<String, Bitmap>(10,1.5f,true));//Last argument true for LRU ordering 
private long size=0;//current allocated size 
private long limit=1000000;//max memory in bytes 

public MemoryCache(){ 
    //use 25% of available heap size 
    setLimit(Runtime.getRuntime().maxMemory()/4); 
} 

public void setLimit(long new_limit){ 
    limit=new_limit; 
    Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB"); 
} 

public Bitmap get(String id){ 
    try{ 
     if(!cache.containsKey(id)) 
      return null; 
     //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78 
     return cache.get(id); 
    }catch(NullPointerException ex){ 
     ex.printStackTrace(); 
     return null; 
    } 
} 

public void put(String id, Bitmap bitmap){ 
    try{ 
     if(cache.containsKey(id)) 
      size-=getSizeInBytes(cache.get(id)); 
     cache.put(id, bitmap); 
     size+=getSizeInBytes(bitmap); 
     checkSize(); 
    }catch(Throwable th){ 
     th.printStackTrace(); 
    } 
} 

private void checkSize() { 
    Log.i(TAG, "cache size="+size+" length="+cache.size()); 
    if(size>limit){ 
     Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated 
     while(iter.hasNext()){ 
      Entry<String, Bitmap> entry=iter.next(); 
      size-=getSizeInBytes(entry.getValue()); 
      iter.remove(); 
      if(size<=limit) 
       break; 
     } 
     Log.i(TAG, "Clean cache. New size "+cache.size()); 
    } 
} 

public void clear() { 
    try{ 
     //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78 
     cache.clear(); 
     size=0; 
    }catch(NullPointerException ex){ 
     ex.printStackTrace(); 
    } 
} 

long getSizeInBytes(Bitmap bitmap) { 
    if(bitmap==null) 
     return 0; 
    return bitmap.getRowBytes() * bitmap.getHeight(); 
} 
} 

Utils

public class Utils { 
public static void CopyStream(InputStream is, OutputStream os) 
{ 
    final int buffer_size=1024; 
    try 
    { 
     byte[] bytes=new byte[buffer_size]; 
     for(;;) 
     { 
      int count=is.read(bytes, 0, buffer_size); 
      if(count==-1) 
       break; 
      os.write(bytes, 0, count); 
     } 
    } 
    catch(Exception ex){} 
} 
} 
+0

Знаете ли вы, что это от ваших собственных изображений? Стектура выглядит так, как будто она исходит из кода Google Maps. – Gus

+0

@Gus да, но посмотрите на первые 4 строки .... Я полагаю, что скоро заканчивается память не может быть выделена карта –

+0

Передумать, чтобы принять правильный ответ :) – Christ

ответ

35

Попробуйте это может помочь вам добавить этот тег в вашем файле манифеста.

<application android:largeHeap="true"> 
</application> 

будет выделять большую кучу для вашего приложения

+0

Кажется, что работает на данный момент, добавив это ... спасибо –

+6

Чтобы быть ясным: ' largeHeap' входит в тег 'application'. Кроме того, он не должен использоваться как исправление утечек памяти, но только тогда, когда вашему приложению действительно требуется больше памяти. См. Https://developer.android.com/training/articles/memory.html – espinchi

+5

Это решение неверно. У вас есть утечки памяти в приложении. Это решение только задержит проблему, но не устранит утечки. –

-1

Попробуйте вызвать System.gc() вручную, прежде чем загружать изображение в память и когда вам не нужно использовать этот объект изображения.

+0

System.gc() не может быть вызван, даже если вы называете это вручную. Даже если он будет вызван, он заморозит приложение в течение секунды. Так что это действительно плохой совет. –

4

Это происходит из-за нехватки памяти, размер файла изображения большой, чтобы решить эту проблему, добавьте этот метод в своем классе:

public static Bitmap decodeImageFile(File f,int WIDTH,int HIGHT){ 
      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_WIDTH=WIDTH; 
       final int REQUIRED_HIGHT=HIGHT; 
       //Find the correct scale value. It should be the power of 2. 
       int scale=1; 
       while(o.outWidth/scale/2>=REQUIRED_WIDTH && o.outHeight/scale/2>=REQUIRED_HIGHT) 
        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; 
      } 

Затем этот метод вызывается, когда вы с помощью этого:

Bitmap b = decodeFile(f); 

использование этого InstEd:

Bitmap b = decodeImageFile(f, 1280, 720); 
+0

А что, если мое изображение больше 1280, 720 ?? Кроме того, я взял изображение в основном из папки с данными и http-запроса ... Каковы изменения в этой категории? –

+0

@FrancescoLombardo, эта проблема решения больших изображений, единственное ограничение - это сделать большое изображение до 1280x720. –

+0

Я решил мою ошибку, используя это разрешение, ваше устройство может поддерживать более 1280x720, тогда вы можете попробовать, вызвав этот метод, используя свой собственный rwsolution –

9

Загрузки Большого Bitmaps Эффективно: http://developer.android.com/training/displaying-bitmaps/load-bitmap.html

Кэширования Bitmaps: http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

Попробуйте изменить размер изображения и рециркуляцию растровое изображение также после использования.

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

public class ImageResizer { 

public static Bitmap decodeSampledBitmapFromFile(String filename, 
    int reqWidth, int reqHeight) { 
// First decode with inJustDecodeBounds=true to check dimensions 
final BitmapFactory.Options 
     options = new BitmapFactory.Options(); 
options.inJustDecodeBounds = true; 
BitmapFactory.decodeFile(filename, options); 
// Calculate inSampleSize 
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); 
// Decode bitmap with inSampleSize set 
options.inJustDecodeBounds = false; 
return BitmapFactory.decodeFile(filename, options); 
} 

    public static int calculateInSampleSize(BitmapFactory.Options options, 
    int reqWidth, int reqHeight) { 
// BEGIN_INCLUDE (calculate_sample_size) 
// 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; 
    } 

    // This offers some additional logic in case the image has a strange 
    // aspect ratio. For example, a panorama may have a much larger 
    // width than height. In these cases the total pixels might still 
    // end up being too large to fit comfortably in memory, so we should 
    // be more aggressive with sample down the image (=larger inSampleSize). 

    long totalPixels = width * height/inSampleSize; 

    // Anything more than 2x the requested pixels we'll sample down further 
    final long totalReqPixelsCap = reqWidth * reqHeight * 2; 

    while (totalPixels > totalReqPixelsCap) { 
     inSampleSize *= 2; 
     totalPixels /= 2; 
    } 
} 
return inSampleSize; 
// END_INCLUDE (calculate_sample_size) 
} 
} 
+0

Можете ли вы взглянуть на это? http://stackoverflow.com/questions/29990186/android-skimagedecoderfactory-only-when-i-read-image-with-extension-png –

-2

Вы можете попробовать этот код ....

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

Bitmap bm = BitmapFactory.decodeFile(img,options); 
imageView.setImageBitmap(bm); 
0
<application 
android:allowBackup="true" 
android:icon="@mipmap/ic_launcher" 
android:label="@string/app_name" 
android:largeHeap="true"/> 
0

OutOfMemoryError является наиболее распространенной проблемой произошла в андроиде, а особенно дело с растровыми изображениями. Эта ошибка генерируется виртуальной машиной Java (JVM), когда объект не может быть выделен из-за нехватки памяти, а также сборщик мусора не может освободить некоторое пространство.

Как уже упоминалось Aleksey, вы можете добавить ниже сущности в файл манифеста android: hardwareAccelerated = "false", android: largeHeap = "true", он будет работать для некоторых окружений.

<application 
    android:allowBackup="true" 
    android:hardwareAccelerated="false" 
    android:icon="@mipmap/ic_launcher" 
    android:label="@string/app_name" 
    android:largeHeap="true" 
    android:supportsRtl="true" 
    android:theme="@style/AppTheme"> 
Смежные вопросы