2016-05-10 99 views
0

好吧,從2天我很掙扎,請有人幫我解決這個問題。從sdcard將圖像加載到gridview拋出內存異常?

我想將外部存儲器中所有圖像的列表加載到我的gridview中。

我能夠使用此方法從系統中獲取圖像列表。

public static ArrayList<String> getFilePaths(Activity context) 
    { 


     Uri u = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; 
     String[] projection = {MediaStore.Images.ImageColumns.DATA}; 
     Cursor c = null; 
     SortedSet<String> dirList = new TreeSet<String>(); 
     ArrayList<String> resultIAV = new ArrayList<String>(); 

     String[] directories = null; 
     if (u != null) 
     { 
      c = context.managedQuery(u, projection, null, null, null); 
     } 

     if ((c != null) && (c.moveToFirst())) 
     { 
      do 
      { 
       String tempDir = c.getString(0); 
       tempDir = tempDir.substring(0, tempDir.lastIndexOf("/")); 
       try{ 
        dirList.add(tempDir); 
       } 
       catch(Exception e) 
       { 

       } 
      } 
      while (c.moveToNext()); 
      directories = new String[dirList.size()]; 
      dirList.toArray(directories); 

     } 

     for(int i=0;i<dirList.size();i++) 
     { 
      File imageDir = new File(directories[i]); 
      File[] imageList = imageDir.listFiles(); 
      if(imageList == null) 
       continue; 
      for (File imagePath : imageList) { 
       try { 

        if(imagePath.isDirectory()) 
        { 
         imageList = imagePath.listFiles(); 

        } 
        if (imagePath.getName().contains(".jpg")|| imagePath.getName().contains(".JPG") 
          || imagePath.getName().contains(".jpeg")|| imagePath.getName().contains(".JPEG") 
          || imagePath.getName().contains(".png") || imagePath.getName().contains(".PNG") 
          || imagePath.getName().contains(".gif") || imagePath.getName().contains(".GIF") 
          || imagePath.getName().contains(".bmp") || imagePath.getName().contains(".BMP") 
          ) 
        { 



         String path= imagePath.getAbsolutePath(); 
         resultIAV.add(path); 

        } 
       } 
       // } 
       catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     } 

     return resultIAV; 


    } 

現在最棘手的部分來了,我知道,因爲它吃了大量的內存,我可以不加載實際圖像,並最終得到一個bitmap Outofmemory exception因此,我有執行的圖像加載器,在一個單獨的執行圖像加載線程和加載圖像異步這裏是圖像加載器。

public class AbstractImageLoader { 
    private static ExecutorService executorService; 
    private static AbstractImageLoader loader; 
    public static int maxWidth; 
    Handler handler;; 
    private Map<ImageView,String> imageViews; 

    static { 
     AbstractImageLoader.executorService = Executors.newFixedThreadPool(5); 
    } 

    public AbstractImageLoader(final Context context) { 
     this.imageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>()); 
     this.handler = new Handler(); 
     AbstractImageLoader.maxWidth = context.getResources().getDisplayMetrics().widthPixels/3; 
    } 

    public static AbstractImageLoader getInstance(final Context context) { 
     if (AbstractImageLoader.loader == null) { 
      AbstractImageLoader.loader = new AbstractImageLoader(context); 
     } 
     return AbstractImageLoader.loader; 
    } 

    private void queuePhoto(final String s, final ImageView imageView, final int n) { 
     AbstractImageLoader.executorService.submit(new PhotosLoader(new PhotoToLoad(s, imageView, n))); 
    } 

    public void DisplayImage(final String s, final ImageView imageView) { 
     this.DisplayImage(s, imageView, -1); 
    } 

    public void DisplayImage(final String s, final ImageView imageView, final int imageResource) { 
     this.imageViews.put(imageView, s); 
     final Bitmap value = MemCache.get(s); 
     if (value != null) { 
      imageView.setImageBitmap(value); 
      return; 
     } 
     this.queuePhoto(s, imageView, imageResource); 
     if (imageResource > -1) { 
      imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 
      imageView.setImageResource(imageResource); 
      return; 
     } 
     imageView.setImageDrawable((Drawable)null); 
    } 

    public void DisplayImage(final String s, final ImageView imageView, final Drawable imageDrawable) { 
     this.imageViews.put(imageView, s); 
     final Bitmap value = MemCache.get(s); 
     if (value != null) { 
      imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); 
      imageView.setImageBitmap(value); 
     } 
     else { 
      this.queuePhoto(s, imageView, -1); 
      if (imageDrawable != null) { 
       imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 
       imageView.setImageDrawable(imageDrawable); 
      } 
     } 
    } 

    public void clearCache() { 
     MemCache.clear(); 
    } 

    public void delete(final String s) { 
     MemCache.remove(s); 
    } 

    protected Bitmap getBitmap(final String ex) { 
     final Bitmap bitmap = null; 


     final File file = new File((String)ex); 
     if (!file.exists()) { 
      final String[] list = file.getParentFile().list(new FileUtils$Filters$6()); 
      Object o = bitmap; 
      if (list != null) { 
       if (list.length != 0) { 
        o = new File(file.getParent(), list[0]); 
        final String absolutePath = ((File)o).getAbsolutePath(); 
        o = ThumbUtils.getThumbnail(absolutePath, AlbumViewLoader.maxWidth); 
        return ThumbUtils.getThumbnail((String)ex, AlbumViewLoader.maxWidth); 
       } 
       o = bitmap; 
      } 
      return (Bitmap)o; 
     } 
     return ThumbUtils.getThumbnail((String)ex, AlbumViewLoader.maxWidth); 
    } 

    boolean imageViewReused(final PhotoToLoad photoToLoad) { 
     final String s = imageViews.get(photoToLoad.imageView); 
     return s == null || !s.equals(photoToLoad.url); 
    } 

    class BitmapDisplayer implements Runnable 
    { 
     Bitmap bitmap; 
     PhotoToLoad photoToLoad; 

     public BitmapDisplayer(final Bitmap bitmap, final PhotoToLoad photoToLoad) { 
      this.bitmap = bitmap; 
      this.photoToLoad = photoToLoad; 
     } 

     @Override 
     public void run() { 
      if (!AbstractImageLoader.this.imageViewReused(this.photoToLoad)) { 
       if (this.bitmap != null) { 
        this.photoToLoad.imageView.setImageBitmap(this.bitmap); 
        this.photoToLoad.imageView.setScaleType(ImageView.ScaleType.CENTER_CROP); 
        return; 
       } 
       if (this.photoToLoad.placeholder_stub > -1) { 
        this.photoToLoad.imageView.setImageResource(this.photoToLoad.placeholder_stub); 
        this.photoToLoad.imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 
       } 
      } 
     } 
    } 

    private class PhotoToLoad 
    { 
     public ImageView imageView; 
     public int placeholder_stub; 
     public String url; 

     public PhotoToLoad(final String url, final ImageView imageView, final int placeholder_stub) { 
      this.url = url; 
      this.imageView = imageView; 
      this.placeholder_stub = placeholder_stub; 
     } 
    } 

    class PhotosLoader implements Runnable 
    { 
     PhotoToLoad photoToLoad; 

     PhotosLoader(final PhotoToLoad photoToLoad) { 
      this.photoToLoad = photoToLoad; 
     } 

     @Override 
     public void run() { 
      try { 
      /* if (!imageViewReused(this.photoToLoad)) { 
        return; 
       }*/ 
       final Bitmap bitmap = AbstractImageLoader.this.getBitmap(this.photoToLoad.url); 
       if (bitmap != null) { 
        MemCache.put(this.photoToLoad.url, bitmap); 
       } 

        AbstractImageLoader.this.handler.post((Runnable)new BitmapDisplayer(bitmap, this.photoToLoad)); 

      } 
      catch (Throwable t) { 
       t.printStackTrace(); 
      } 
     } 
    } 
} 

這裏是我創建的工具類的位圖:

public static Bitmap getThumbnail(final String s, int n) { 
     final BitmapFactory.Options bitmapFactory$Options = new BitmapFactory.Options(); 
     bitmapFactory$Options.inSampleSize = 1; 
     bitmapFactory$Options.inJustDecodeBounds = true; 
     BitmapFactory.decodeFile(s, bitmapFactory$Options); 
     if (bitmapFactory$Options.mCancel || bitmapFactory$Options.outWidth == -1 || bitmapFactory$Options.outHeight == -1) { 
      return null; 
     } 
     final int outHeight = bitmapFactory$Options.outHeight; 
     final int outWidth = bitmapFactory$Options.outWidth; 
     final float max = Math.max(n/outWidth, n/outHeight); 
     n = (int)(outWidth * max); 
     final int n2 = (int)(outHeight * max); 
     bitmapFactory$Options.inSampleSize = calculateInSampleSize(bitmapFactory$Options, n, n); 
     bitmapFactory$Options.inJustDecodeBounds = false; 
     bitmapFactory$Options.inDither = true; 
     bitmapFactory$Options.inPreferredConfig = Bitmap.Config.RGB_565; 
     Bitmap bmp = BitmapFactory.decodeFile(s, bitmapFactory$Options); 
     return ThumbnailUtils.extractThumbnail(bmp, n, n, 2); 
    } 

因此,這裏是我的問題:

1.In我的縮略圖工具方法計算:

final float max = Math.max(n/outWidth, n/outHeight); 
      n = (int)(outWidth * max); 
      final int n2 = (int)(outHeight * max); 

n總是返回爲0.因此縮略圖沒有被加載,所以暫時我硬編碼到212和縮略圖加載啓動

但加載特定數量的圖像後,我仍然得到out of memory exception

這裏是logcat的:

java.lang.OutOfMemoryError: Failed to allocate a 347790 byte allocation with 294944 free bytes and 288KB until OOM 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at dalvik.system.VMRuntime.newNonMovableArray(Native Method) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:639) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:615) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:391) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at com.geekmk.audiofx.test.ThumbUtils.getThumbnail(ThumbUtils.java:66) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at com.geekmk.audiofx.test.AbstractImageLoader.getBitmap(AbstractImageLoader.java:110) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at com.geekmk.audiofx.test.AbstractImageLoader$PhotosLoader.run(AbstractImageLoader.java:171) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 
05-10 09:57:19.448 22498-22683/com.geekmk.audiofx W/System.err:  at java.lang.Thread.run(Thread.java:818) 

請幫我 在此先感謝。

+0

你真的幾乎沒有堆空間。加載較少的圖像。在可以的地方回收'Bitmap'對象(參見'BitmapFactory.Options'的'inBitmap')。允許「Bitmap」對象在不再需要時被垃圾回收,並且不能被回收。 – CommonsWare

+0

回收Bitmap對象?你能給我一個參考鏈接或解釋如何解決這個問題嗎? – Manikanta

+0

http://developer.android.com/training/displaying-bitmaps/manage-memory.html http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inBitmap – CommonsWare

回答

0

n爲始終返回0。因此縮略圖是沒有得到加載,所以 時間被我把它硬編碼爲212和縮略圖加載啓動

這是因爲你混合廉政局和浮動的。嘗試更改此代碼以使用浮動。

public static Bitmap getThumbnail(final String s, float n) { 

final float outHeight = bitmapFactory$Options.outHeight; 
final float outWidth = bitmapFactory$Options.outWidth; 

不知道爲什麼你使用的是最終在那裏,我不認爲這是必要的。

而使用諸如Picasso之類的東西將爲您節省大量的代碼和時間。

您可以從您的片段做這樣的事情還是ViewHolder

Picasso.with(context).load(R.drawable.landing_screen).into(imageView1); 
Picasso.with(context).load("file:///android_asset/DvpvklR.png").into(imageView2); 
Picasso.with(context).load(new File(...)).into(imageView3); 
+0

畢加索是不會幫助我的,因爲我已經嘗試過,我處理的圖像是本地sd卡圖像,並且數量巨大 – Manikanta

+0

不知道爲什麼這樣做沒有幫助,我提供的代碼片段允許您給畢加索文件位置從您的本地SD卡:Picasso.with(context).load(new File(...))。到(imageView3);畢加索可以幫助您根據需要加載它們,而不是一次加載它們。 – shredder

+0

我已經使用畢加索,所以我最後的Android項目的網址加載,但不知道它是否可以幫助我加載本地圖像,我試圖加載圖像在我的適配器onBindHolder(),但因爲它是在我的主線程掛它,所以任何解決方案? – Manikanta

0

我用下面的方法越來越被縮小到不拋出OOM異常位圖:

private static Bitmap getBitmapFromUri(@NonNull Context context, @NonNull Uri uri) throws IOException { 

     ParcelFileDescriptor parcelFileDescriptor = 
       context.getContentResolver().openFileDescriptor(uri, "r"); 
     assert parcelFileDescriptor != null; 
     FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor(); 
     Bitmap image = BitmapFactory.decodeFileDescriptor(fileDescriptor); 
     parcelFileDescriptor.close(); 

     TinyDB tinyDB = new TinyDB(context); 
     int maxSize = Math.min(tinyDB.getInt(AppConstants.DEVICE_WIDTH, 720), tinyDB.getInt(AppConstants.DEVICE_HEIGHT, 1080)); 
     int outWidth; 
     int outHeight; 
     int inWidth = image.getWidth(); 
     int inHeight = image.getHeight(); 
     if(inWidth > inHeight){ 
      outWidth = maxSize; 
      outHeight = (inHeight * maxSize)/inWidth; 
     } else { 
      outHeight = maxSize; 
      outWidth = (inWidth * maxSize)/inHeight; 
     } 

     return Bitmap.createScaledBitmap(image, outWidth, outHeight, false); 
    } 

在代碼,TinyDB是一個圍繞SharedPreferences的包裝,並且我在應用程序啓動後立即以像素爲單位存儲屏幕大小。