2011-08-09 55 views
3

我正在做图像上的应用程序以在GridView中显示它们,我从服务器获取20张图像。每个图像的解析度为720 * 540.我使用JSON解析来提取网址,并使用下面的代码转换成位图来设置图像。应用程序在Android中的图像速度很慢

public static Bitmap loadImageFromUrl(String url) { 
    InputStream inputStream;Bitmap b; 
    try { 
     inputStream = (InputStream) new URL(url).getContent(); 
     BitmapFactory.Options bpo= new BitmapFactory.Options(); 
     if(bpo.outWidth>500) { 
      bpo.inSampleSize=8; 
      b=BitmapFactory.decodeStream(inputStream, null,bpo); 
     } else { 
      bpo.inSampleSize=2; 
      b=BitmapFactory.decodeStream(inputStream, null,bpo); 
     } 
     return b; 
    } catch (IOException e) { 
     throw new RuntimeException(e); 
    } 
} 

我的应用工作正常,但它花费太多时间来加载图像。所以我的应用程序变得缓慢。我应该减少图像的分辨率吗?

如何解决问题?

回答

7

如果你正在做一个网格视图加载这样的分辨率的20幅图片,我建议如下:

  1. 绝对减少图像的大小。除非你的目标是平板电脑,否则你会好起来的,因为大多数智能手机无法用20张图片来达到这个分辨率。

  2. 如果可以,缓存图像。

  3. 在不同的线程下载图像。存储一个HashMap会让你很容易,只需将所有图像视图与图像文件名或其他形式的ID作为关键字。下载图像时将消息发送给您的处理程序,并在解码后更新视图。您可以直接检索您的视图。请记住检查他们是否仍在窗口中。这样图像就会迅速显示出来。我不认为多线程的图像会有所帮助,只要确保使用另一个线程来“推送图像”和主UI线程更新。那么用户体验将大大提高。

希望这会有所帮助。

---一些实现,我没有完整的代码我现在---

有一个数据结构与进来的数据相匹配的意见。非常方便在这里。

private HashMap<String,ImageView> pictures; 

当你得到图像的URL列表,遍历它们:

pictures.put(id,view); 
     try{ 
      FileInputStream in = openFileInput(id); 
      Bitmap bitmap = null; 
      bitmap = BitmapFactory.decodeStream(in, null, null); 
     view.setImageBitmap(bitmap); 
     }catch(Exception e){ 
      new Thread(new PictureGetter(this,mHandler,id)).start(); 
     } 

(这里的图片吸气只会取如果尚未高速缓存的图像,并将它缓存)

代码以更新图像视图:

if(id!=null){ 
     ImageView iv = pictures.get(id); 
     if(iv!=null){ 
      try{ 
       FileInputStream in = openFileInput(id); 
       Bitmap bitmap = null; 
       bitmap = BitmapFactory.decodeStream(in, null, null); 
       iv.setImageBitmap(bitmap); 
      }catch(Exception e){ 
     } 
    } 
+0

我用异步的方式来获取图像。 – Abhi

+0

如果您显示的图像很多,您不能在网格视图上显示缩略图吗?获取所有这些后,您将开始在背景中获取一些原始分辨率,所以如果点击该分辨率,它将被提取。 – Edison

+0

是的这个想法是好的,是否有可能通过程序 – Abhi

1

我猜测大部分的加载时间是由于大量的图像结合图像的大小。

有2级可能的解决方案:

  1. 调整大小的图像,或者使得文件大小低于75KB左右降低图像的质量。

  2. 使用多线程一次检索多个图像。如果用户的连接速度很慢,这可能没有帮助,但如果将它与足够小的文件组合起来,它可能只是足够帮助。您可能想要确定设备当前的带宽是多少,并根据您运行的线程数量来确定。

例如:20张75KB的图像和200 KB/s = 3或4个并发线程的可用连接。

希望这会有所帮助。

+0

谢谢你的回复,你能告诉我如何使用第二个选项在你的答案请 – Abhi

+0

我使用异步的方式,如果你有一个应用的想法布拉姆给你造成问题来获取所有图像 – Abhi

+1

@Titus另一个问题,并告诉我们你已经完成的代码以及你的问题在哪里,如果你有特定的问题,我们只能帮你解决问题。 Stackoverflow不会简单地给你你需要复制和粘贴的代码。 – Janusz

0
public class clothImageLoader { 

// the simplest in-memory cache implementation. This should be replaced with 
// something like SoftReference or BitmapOptions.inPurgeable(since 1.6) 
// public static HashMap<String, Bitmap> cache = new HashMap<String, 
// Bitmap>(); 

private static File cacheDir; 

public clothImageLoader(Context context) { 
    // Make the background thead low priority. This way it will not affect 
    // the UI performance 
    photoLoaderThread.setPriority(Thread.NORM_PRIORITY - 1); 

    // 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"); 
     cacheDir = new File(ConstValue.MY_ClothBitmap_DIR); 
    else 
     cacheDir = context.getCacheDir(); 
    if (!cacheDir.exists()) 
     cacheDir.mkdirs(); 
} 

final int stub_id = R.drawable.icon; 

public void DisplayImage(String url, Activity activity, ImageView imageView) { 
    if (ConstValue.ClothRoomcache.containsKey(url)) 
     imageView.setImageBitmap(ConstValue.ClothRoomcache.get(url)); 
    else { 
     queuePhoto(url, activity, imageView); 
     imageView.setImageResource(stub_id); 
    } 
} 

private void queuePhoto(String url, Activity activity, ImageView imageView) { 
    // This ImageView may be used for other images before. So there may be 
    // some old tasks in the queue. We need to discard them. 
    photosQueue.Clean(imageView); 
    PhotoToLoad p = new PhotoToLoad(url, imageView); 
    synchronized (photosQueue.photosToLoad) { 
     photosQueue.photosToLoad.push(p); 
     photosQueue.photosToLoad.notifyAll(); 
    } 

    // start thread if it's not started yet 
    if (photoLoaderThread.getState() == Thread.State.NEW) 
     photoLoaderThread.start(); 
} 

private Bitmap getBitmap(String url) { 
    // I identify images by hashcode. Not a perfect solution, good for the 
    // demo. 
    String filename = String.valueOf(url.hashCode()); 
    File f = new File(cacheDir, filename); 

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

    // from web 
    try { 
     Bitmap bitmap = null; 
     /* 
     * InputStream is=new URL(url).openStream(); OutputStream os = new 
     * FileOutputStream(f); Utils.CopyStream(is, os); os.close(); 
     */ 
     URL url1 = new URL(url); 
     bitmap = decodeFile(f); 
     /* Open a connection to that URL. */ 
     URLConnection ucon = url1.openConnection(); 

     /* 
     * Define InputStreams to read from the URLConnection. 
     */ 
     InputStream is = ucon.getInputStream(); 
     // FlushedInputStream a = new FlushedInputStream(is); 
     BufferedInputStream bis = new BufferedInputStream(is); 

     /* 
     * Read bytes to the Buffer until there is nothing more to read(-1). 
     */ 
     ByteArrayBuffer baf = new ByteArrayBuffer(5000); 
     int current = 0; 
     while ((current = bis.read()) != -1) { 
      baf.append((byte) current); 
     } 

     /* Convert the Bytes read to a String. */ 
     FileOutputStream fos = new FileOutputStream(f); 
     fos.write(baf.toByteArray()); 
     fos.flush(); 
     fos.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 = ConstValue.bmpSize; 
     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++; 
     } 

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

PhotosQueue photosQueue = new PhotosQueue(); 

public void stopThread() { 
    photoLoaderThread.interrupt(); 
} 

// stores list of photos to download 
class PhotosQueue { 
    private Stack<PhotoToLoad> photosToLoad = new Stack<PhotoToLoad>(); 

    // removes all instances of this ImageView 
    public void Clean(ImageView image) { 
     for (int j = 0; j < photosToLoad.size();) { 
      if (photosToLoad.get(j).imageView == image) 
       photosToLoad.remove(j); 
      else 
       ++j; 
     } 
    } 
} 

class PhotosLoader extends Thread { 
    public void run() { 
     try { 
      while (true) { 
       // thread waits until there are any images to load in the 
       // queue 
       if (photosQueue.photosToLoad.size() == 0) 
        synchronized (photosQueue.photosToLoad) { 
         photosQueue.photosToLoad.wait(); 
        } 
       if (photosQueue.photosToLoad.size() != 0) { 
        PhotoToLoad photoToLoad; 
        synchronized (photosQueue.photosToLoad) { 
         photoToLoad = photosQueue.photosToLoad.pop(); 

         // photoToLoad=photosQueue.photosToLoad.get(0); 
         // photosQueue.photosToLoad.remove(photoToLoad); 
        } 
        Bitmap bmp = getBitmap(photoToLoad.url); 
        ConstValue.ClothRoomcache.put(photoToLoad.url, bmp); 
        if (((String) photoToLoad.imageView.getTag()).equals(photoToLoad.url)) { 
         BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad.imageView); 
         Activity a = (Activity) photoToLoad.imageView.getContext(); 
         a.runOnUiThread(bd); 
        } 
       } 
       if (Thread.interrupted()) 
        break; 
      } 
     } catch (InterruptedException e) { 
      // allow thread to exit 
     } 
    } 
} 

PhotosLoader photoLoaderThread = new PhotosLoader(); 

// Used to display bitmap in the UI thread 
class BitmapDisplayer implements Runnable { 
    Bitmap bitmap; 
    ImageView imageView; 

    public BitmapDisplayer(Bitmap b, ImageView i) { 
     bitmap = b; 
     imageView = i; 
    } 

    public void run() { 
     if (bitmap != null) 
      imageView.setImageBitmap(bitmap); 
     else 
      imageView.setImageResource(stub_id); 
    } 
} 

public static void clearCache() { 
    // clear memory cache 
    ConstValue.ClothRoomcache.clear(); 

    // clear SD cache 
    File[] files = cacheDir.listFiles(); 
    for (File f : files) 
     f.delete(); 
} 

public class FlushedInputStream extends FilterInputStream { 
    public FlushedInputStream(InputStream inputStream) { 
     super(inputStream); 
    } 

    @Override 
    public long skip(long n) throws IOException { 
     long totalBytesSkipped = 0L; 
     while (totalBytesSkipped < n) { 
      long bytesSkipped = in.skip(n - totalBytesSkipped); 
      if (bytesSkipped == 0L) { 
       int a = read(); 
       if (a < 0) { 
        break; // we reached EOF 
       } else { 
        bytesSkipped = 1; // we read one byte 
       } 
      } 
      totalBytesSkipped += bytesSkipped; 
     } 
     return totalBytesSkipped; 
    } 
} 

} 

当你调用该方法,在GridView getView方法:

holder.image.setTag(ChoseInfo.get(position).getLink()); 
     imageLoader.DisplayImage(ChoseInfo.get(position).getLink(), activity, holder.image); 

ChoseInfo.get(位置).getLink())

这里getLink()是互联网链接。

0

Picasso library

解决方案是不是使用位图载入图片直接使用了一个名为毕加索真棒图书馆的只是超级快,我知道你真的爱这个你可以做到这一点像这样

添加毕加索JAR文件(这里下载毕加索jar文件)项目中使用毕加索加载图片喜欢这张

Picasso.with(context).load(new File(title)).centerCrop() 
    .resize(150, 150).error(R.drawable.ic_launcher).into(image); 

其中标题是要加载图片的路径。裁剪,调整大小,错误是可选的。

1

我在我的android应用程序中有同样的问题。当你从一个大尺寸的图像解码一个位图并设置为imageBitmap到一个图像视图时,你的应用程序可能会变慢,经过几次尝试,你会得到一个“内存不足的例外”

两种可能的方式请尝试处理此问题: 1-从文件中解码时减小位图大小 2-使用图像库。

我喜欢第二种方式,并使用通用图像加载器。 https://github.com/nostra13/Android-Universal-Image-Loader

String url = "file://" + your_file_path 
com.nostra13.universalimageloader.core.ImageLoader.getInstance().displayImage(url, ivPicture, options); 
相关问题