2014-05-20 4 views
9

Ich benutze universal-image-loader Bibliothek, um Bilder zu laden, aber wenn ich copy() auf eine geladene Bitmap-Datei aufrufen, bekomme ich in einigen Fällen OutOfMemoryError. Hier ist mein Code:bitmap.copy() löst nicht genügend Speicher aus

ImageLoader.getInstance().loadImage(path, new ImageLoadingListener() { 

     @Override 
     public void onLoadingStarted(String arg0, View arg1) { 
      // TODO Auto-generated method stub 

     } 

     @Override 
     public void onLoadingFailed(String arg0, View arg1, FailReason arg2) { 
      // TODO Auto-generated method stub 

     } 

     @Override 
     public void onLoadingComplete(String arg0, View arg1, Bitmap arg2) { 
      bm = arg2; 
     } 

     @Override 
     public void onLoadingCancelled(String arg0, View arg1) { 
      // TODO Auto-generated method stub 

     } 
    }); 
Bitmap bm2= bm.copy(Bitmap.Config.ARGB_8888, true); //where the crash happens 

ich die zweite Bitmap nicht als wandelbar brauchen daher kann ich es zeichnen.

+0

Diese Fälle können sein, wenn Bilder groß sind. Wie groß sind Ihre Bilder? – Emmanuel

+0

Sind Sie sicher, dass Sie andere Bitmaps nicht im Speicher behalten, obwohl dies nicht sinnvoll ist? (Das passiert oft) - Dieses Dokument kann auch hilfreich sein: http://developer.android.com/training/displaying-bitmaps/manage-memory.html – Shlublu

+0

Nun, ich benutze den Bildlader, um die Bilder zu laden, die ich nie hatte Probleme damit. Das Problem tritt nur beim Aufruf von copy() auf der Bitmap auf. – user1940676

Antwort

13

Zunächst einmal versuchen, ein wenig Zeit zu finden, gute offizielle Dokumentation über Bitmaps zu lesen: Displaying Bitmaps Efficiently

Es wird Ihnen zu verstehen, warum und wann java.lang.OutofMemoryError passiert. Und wie man es vermeidet.

Was ist mit Ihrer Frage: In diesem Artikel finden: Android: convert Immutable Bitmap into Mutable

But from API Level 11 only options.inMutable available to load the file into a mutable bitmap.

So, if we are building application with API level less than 11, then we have to find some other alternatives.

One alternative is creating another bitmap by copying the source

bitmap. mBitmap = mBitmap.copy(ARGB_8888 ,true);

But the will throw OutOfMemoryException if the source file is big. Actually incase if we want to edit an original file, then we will face this issue. We should be able to load at-least image into memory, but most we can not allocate another copy into memory.

So, we have to save the decoded bytes into some where and clear existing bitmap, then create a new mutable bitmap and load back the saved bytes into bitmap again. Even to copy bytes we cannot create another ByteBuffer inside the memory. In that case need to use MappedByteBuffer that will allocate bytes inside a disk file.

Following code would explain clearly:

//this is the file going to use temporally to save the bytes. 

File file = new File("/mnt/sdcard/sample/temp.txt"); 
file.getParentFile().mkdirs(); 

//Open an RandomAccessFile 
/*Make sure you have added uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" 
into AndroidManifest.xml file*/ 
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw"); 

// get the width and height of the source bitmap. 
int width = bitmap.getWidth(); 
int height = bitmap.getHeight(); 

//Copy the byte to the file 
//Assume source bitmap loaded using options.inPreferredConfig = Config.ARGB_8888; 
FileChannel channel = randomAccessFile.getChannel(); 
MappedByteBuffer map = channel.map(MapMode.READ_WRITE, 0, width*height*4); 
bitmap.copyPixelsToBuffer(map); 
//recycle the source bitmap, this will be no longer used. 
bitmap.recycle(); 
//Create a new bitmap to load the bitmap again. 
bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888); 
map.position(0); 
//load it back from temporary 
bitmap.copyPixelsFromBuffer(map); 
//close the temporary file and channel , then delete that also 
channel.close(); 
randomAccessFile.close(); 

Und here ist Beispielcode. out of memory Ausnahme an allen

+1

Ich bekomme immer noch einen OutOfMemoryError mit dieser Lösung. Es passiert in dieser Zeile: 'bitmap = Bitmap.createBitmap (Breite, Höhe, Config.ARGB_8888);' – Andy

+0

bitmap = Bitmap.createScaledBitmap (originalBitmap, width, height, true); – Faakhir

+1

Die Beispielcodeverbindung ist unterbrochen – lxknvlk

3

Sie können nicht viel über den Bitmap-Outofmemory-Fehler hinaus tun, außer sicherzustellen, dass die Bitmap, die Sie kopieren oder anzeigen, nicht sehr groß ist. Glücklicherweise verfügt der universelle Image-Loader über eine Funktion zum Komprimieren von Bitmap, indem die Konfiguration geändert wird. Gib Bitmap.Config.RGG_565 also einen Versuch. Es soll den Speicherbedarf der Bitmap halbieren. Sie können auch eine große Heap-Größe anfordern. Sie können auch die skalierte Version der Bitmap kopieren.

2

Wie Illegel Argument gesagt, müssen Sie sicherstellen, dass die Bitmap nicht zu groß ist. Stellen Sie außerdem sicher, dass Sie jeweils nur eine Bitmap in den Speicher laden.

Sie können dynamisch die Bitmap skalieren dankbar BitmapFactory

Bitmap b = BitmapFactory.decodeByteArray(imageAsBytes, 0, imageAsBytes.length) 
image.setImageBitmap(Bitmap.createScaledBitmap(b, 300, 300, false)); 
2

verwenden, dass es auf Ihrem Gerät geschieht, und zwar nicht nur auf dem Benutzer-Geräte.

1) Das ist etwas, mit dem Sie umgehen müssen und auf das Sie angemessen reagieren. Fehlermeldung anzeigen oder niedrigere Auflösung der Bitmap laden. Ihre App wird auf vielen verschiedenen Geräten ausgeführt, die jeweils unterschiedliche Speicherkapazitäten haben.

2) Verwenden Sie nach jeder Operation die wichtige Funktion Bitmap.recycle, die Ihre alte Bitmap überflüssig macht. Dadurch wird sofort Speicher für die nächste Arbeit freigegeben, ohne dass GC ausgeführt werden muss und möglicherweise nicht genügend Arbeitsspeicher vorhanden ist.

+0

Ja, das ist ein guter Rat, aber [erinnern] (http://developer.android.com/training/displaying-bitmaps/manage-memory.html#recycle): Sie sollten 'recycle()' nur verwenden, wenn Sie es sind Vergewissern Sie sich, dass die Bitmap nicht mehr verwendet wird. Wenn Sie 'recycle()' aufrufen und später versuchen, die Bitmap zu zeichnen, erhalten Sie den Fehler: '" Canvas: versucht, eine recycelte Bitmap zu verwenden "'. –

+0

Sicher, die Doktoren schreiben es klar. –

1

Verwenden sie diesen Code Ihr Zweck url

Nehmen sie die folgenden Klassen in Ihrem Code und in den letzten Einsatz Imageloader erfüllen laden geben es url, Imageview und dra wable zu zeigen, dass incase URL kein Bild zurückgibt

FileCache.java

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(),"LazyList"); 
    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.java

public class MemoryCache { 
private Map<String, SoftReference<Bitmap>> cache=Collections.synchronizedMap(new HashMap<String, SoftReference<Bitmap>>()); 

public Bitmap get(String id){ 
    if(!cache.containsKey(id)) 
     return null; 
    SoftReference<Bitmap> ref=cache.get(id); 
    return ref.get(); 
} 

public void put(String id, Bitmap bitmap){ 
    cache.put(id, new SoftReference<Bitmap>(bitmap)); 
} 

public void clear() { 
    cache.clear(); 
} 
} 

Utils.java

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){} 
} 
} 

ImageLoader.java

public class ImageLoader { 

MemoryCache memoryCache=new MemoryCache(); 
FileCache fileCache; 
private Map<ImageView, String> imageViews=Collections.synchronizedMap(new WeakHashMap<ImageView, String>()); 
ExecutorService executorService; 

public ImageLoader(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 getBitmap(String url) 
{ 
    File f=fileCache.getFile(url); 

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

    //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 (Exception ex){ 
     ex.printStackTrace(); 
     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(); 
} 

} 

nennen diesen Code, wo Sie zwischenzuspeichern oder herunterladen möchten oder verwalten Bilder

imageLoader.DisplayImage(song.get(CustomizedListView.KEY_THUMB_URL), thumb_image);