高效地加载图片(三) 缓存图片

如果只需要加载一张图片,那么直接加载就可以.但是,如果要在类似ListView,GridView或者ViewPager的控件中加载大量的图片时,问题就会变得复杂.在使用这类控件时,在短时间内可能会显示在屏幕上的图片数量是不固定的.

这类控件会通过子View的复用来保持较低的内存占用.而Garbage
Collector也会在View被复用时释放对应的Bitmap,保证这些没用用到的Bitmap不会长期存在于内存中.但是为了保证控件的流畅滑动,在一个View再次滑动出现在屏幕上时,我们需要避免图片的重复性加载.而此时,在内存和磁盘上开辟一块缓存空间往往能够保证图片的快速重复加载.

使用内存缓存

一块内存缓存在耗费一定应用内存基础上,能够让快速加载图片成为可能.而LruCache正合适用来缓存图片,对最近使用过的对象保存在LinkedHashMap中,并且将最近未使用过的对象释放.

为了给LrcCache确定一个合适的大小,有以下一些因素需要考虑:

1.应用中其他组件占用内存的情况

2.有多少图片可能会显示在屏幕上?有多少图片将要显示在屏幕上?

3.屏幕的尺寸和屏幕密度是多少?与Nexus S这类高屏幕密度设备相比,Galaxy
Nexs这类超高屏幕密度的设备,往往需要更大的缓存空间来存储相同数量的图片.

4.图片的尺寸以及其他的参数,还有每张图片将会占用多少内存.

5.图片被访问的频率有多高?是否有一些图片的访问频率会比另外一些更高?如果是这样,我们可能需要将一些图片长存于内存中,或者使用多个LrcCache来对不同的Bitmap进行分组.

6.我们还需要在图片的数量和质量之间权衡.有些时候,在缓存中存放大量的缩略图,而在后台加载高清图片会明显提高效率.

对每个应用来说,需要指定的缓存大小是不一定的,这取决于我们对应用的分析并得出相应的解决方案.如果缓存空间过小,可能会造成额外的开销,这对整个应用并无补益;而缓存空间过大,则可能会造成java.lang.OutOfMemory异常,并且留给其他组件使用的内存空间也会相应减少.

以下为初始化一个存放Bitmap的LrcCache的例子:

view plaincopy to clipboardprint?

  1. private LruCache<String, Bitmap> mMemoryCache;
  2. @Override

  3. protected void onCreate(Bundle savedInstanceState) {

  4. ...

  5. // 获取最大的可用空间,如果需要的空间超出这个大小,则会抛出OutOfMemory异常

  6. // LrcCache构造函数中的参数是以千字节为单位的

  7. final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
  8. // 此处缓存大小取可用内存的1/8

  9. final int cacheSize = maxMemory / 8;
  10. mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {

  11. @Override

  12. protected int sizeOf(String key, Bitmap bitmap) {

  13. // The cache size will be measured in kilobytes rather than

  14. // number of items.

  15. // 缓存的大小会使用千字节来衡量

  16. return bitmap.getByteCount() / 1024;

  17. }

  18. };

  19. ...

  20. }
  21. // 将Bitmap存入缓存

  22. public void addBitmapToMemoryCache(String key, Bitmap bitmap) {

  23. if (getBitmapFromMemCache(key) == null) {

  24. // 当使用(getBitmapFromMemCache方法,根据传入的key获取Bitmap

  25. // 当获取到的Bitmap为空时,证明没有存储过该Bitmap

  26. // 此时将该Bitmap存储到LrcCache中

  27. mMemoryCache.put(key, bitmap);

  28. }

  29. }
  30. // 根据key从LrcCache中获取对应的Bitmap

  31. public Bitmap getBitmapFromMemCache(String key) {

  32. return mMemoryCache.get(key);

  33. }

private LruCache<String, Bitmap> mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
...
// 获取最大的可用空间,如果需要的空间超出这个大小,则会抛出OutOfMemory异常
// LrcCache构造函数中的参数是以千字节为单位的
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

// 此处缓存大小取可用内存的1/8
final int cacheSize = maxMemory / 8;

mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
// 缓存的大小会使用千字节来衡量
return bitmap.getByteCount() / 1024;
}
};
...
}

// 将Bitmap存入缓存
public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
if (getBitmapFromMemCache(key) == null) {
// 当使用(getBitmapFromMemCache方法,根据传入的key获取Bitmap
// 当获取到的Bitmap为空时,证明没有存储过该Bitmap
// 此时将该Bitmap存储到LrcCache中
mMemoryCache.put(key, bitmap);
}
}

// 根据key从LrcCache中获取对应的Bitmap
public Bitmap getBitmapFromMemCache(String key) {
return mMemoryCache.get(key);
}

注意:在这个例子中,应用内存的1/8被分配用作缓存.在一台正常/高屏幕分辨率的设备上,这个缓存的大小在4MB左右(32/8
MB).而使用800×480分辨率的图片填充一个全屏的GridView的话,大概需要1.5MB的内存空间(800*480*4
bytes),所以这个缓存能够存储至少2.5页的图片.

在加载一张图片到ImageView时,LrcCache会首先检查这张图片是否存在.如果图片存在,则图片会立即被更新到ImageView中,否则会开启一个后台线程去加载这张图片.

view plaincopy to clipboardprint?

  1. public void loadBitmap(int resId, ImageView imageView) {

  2. // 将图片的资源id转换为String型,作为key

  3. final String imageKey = String.valueOf(resId);
  4. // 根据key从LruCache中获取Bitmap

  5. final Bitmap bitmap = getBitmapFromMemCache(imageKey);

  6. if (bitmap != null) {

  7. // 如果获取到的Bitmap不为空

  8. // 则直接将获取到的Bitmap更新到ImageView中

  9. mImageView.setImageBitmap(bitmap);

  10. } else {

  11. // 否则,则先在ImageView中设置一张占位图

  12. mImageView.setImageResource(R.drawable.image_placeholder);

  13. // 再开启一个新的异步任务去加载图片

  14. BitmapWorkerTask task = new BitmapWorkerTask(mImageView);

  15. task.execute(resId);

  16. }

  17. }

public void loadBitmap(int resId, ImageView imageView) {
// 将图片的资源id转换为String型,作为key
final String imageKey = String.valueOf(resId);

// 根据key从LruCache中获取Bitmap
final Bitmap bitmap = getBitmapFromMemCache(imageKey);
if (bitmap != null) {
// 如果获取到的Bitmap不为空
// 则直接将获取到的Bitmap更新到ImageView中
mImageView.setImageBitmap(bitmap);
} else {
// 否则,则先在ImageView中设置一张占位图
mImageView.setImageResource(R.drawable.image_placeholder);
// 再开启一个新的异步任务去加载图片
BitmapWorkerTask task = new BitmapWorkerTask(mImageView);
task.execute(resId);
}
}

BitmapWorkerTask也需要更新,将Bitmap以键值对的形式存储到LrcCache中.

view plaincopy to clipboardprint?

  1. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {

  2. ...

  3. // 在后台加载图片

  4. @Override

  5. protected Bitmap doInBackground(Integer... params) {

  6. final Bitmap bitmap = decodeSampledBitmapFromResource(

  7. getResources(), params[0], 100, 100));

  8. // 将Bitmap对象以键值对的形式存储到LrcCache中

  9. addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);

  10. return bitmap;

  11. }

  12. ...

  13. }

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// 在后台加载图片
@Override
protected Bitmap doInBackground(Integer... params) {
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100));
// 将Bitmap对象以键值对的形式存储到LrcCache中
addBitmapToMemoryCache(String.valueOf(params[0]), bitmap);
return bitmap;
}
...
}

使用磁盘缓存

内存缓存在访问最近使用过的图片方面能够极大地提高效率,但是我们不能指望所有需要的图片都能在内存缓存中找到.向GridView这类数据源中有大量数据的控件,会轻易的就将内存缓存占用满.而我们的应用也可能会被其他的任务打断(切换到后台),例如接听电话,而当我们的应用被切换到后台时,它极有可能会被关闭,此时内存缓存也会被销毁.当用户返回我们的应用时,应用又需要重新加载需要的图片.

而磁盘缓存会在内存缓存被销毁时继续加载图片,这样当内存缓存不可用但是又需要加载图片时就能够减少加载的时间.当然,从磁盘上读取图片要比从内存中读取图片慢,而且需要在后台线程中执行,因为图片的加载时间是不一定的.

注意:如果缓存图片需要经常访问,则将这些缓存图片存储到ContentProvider是一个更好的选择,例如图库应用就是这么做的.

以下示例是一个DiskLruCache的实现(Android
source
).这个示例是在内存缓存的基础上又增加了磁盘缓存.

view plaincopy to clipboardprint?

  1. private DiskLruCache mDiskLruCache;

  2. private final Object mDiskCacheLock = new Object();

  3. private boolean mDiskCacheStarting = true;

  4. private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB

  5. private static final String DISK_CACHE_SUBDIR = "thumbnails";
  6. @Override

  7. protected void onCreate(Bundle savedInstanceState) {

  8. ...

  9. // 初始化内存缓存

  10. ...

  11. // 在后台线程初始化磁盘缓存

  12. File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);

  13. new InitDiskCacheTask().execute(cacheDir);

  14. ...

  15. }
  16. class InitDiskCacheTask extends AsyncTask<File, Void, Void> {

  17. @Override

  18. protected Void doInBackground(File... params) {

  19. synchronized (mDiskCacheLock) {

  20. File cacheDir = params[0];

  21. mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);

  22. mDiskCacheStarting = false; // 标识结束初始化

  23. mDiskCacheLock.notifyAll(); // 唤醒等待中的线程

  24. }

  25. return null;

  26. }

  27. }
  28. class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {

  29. ...

  30. // 在后台解析图片

  31. @Override

  32. protected Bitmap doInBackground(Integer... params) {

  33. final String imageKey = String.valueOf(params[0]);
  34. // 在后台线程中判断图片是否已经存在于磁盘缓存中

  35. Bitmap bitmap = getBitmapFromDiskCache(imageKey);
  36. if (bitmap == null) { // 不存在于磁盘缓存中

  37. // 则正常加载图片

  38. final Bitmap bitmap = decodeSampledBitmapFromResource(

  39. getResources(), params[0], 100, 100));

  40. }
  41. // 将加载出的图片添加到缓存中

  42. addBitmapToCache(imageKey, bitmap);
  43. return bitmap;

  44. }

  45. ...

  46. }
  47. public void addBitmapToCache(String key, Bitmap bitmap) {

  48. // 将图片添加到内存缓存中

  49. if (getBitmapFromMemCache(key) == null) {

  50. mMemoryCache.put(key, bitmap);

  51. }
  52. // 同时将图片添加到磁盘缓存中

  53. synchronized (mDiskCacheLock) {

  54. if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {

  55. mDiskLruCache.put(key, bitmap);

  56. }

  57. }

  58. }
  59. public Bitmap getBitmapFromDiskCache(String key) {

  60. synchronized (mDiskCacheLock) {

  61. // 当磁盘缓存正在初始化时,则等待

  62. while (mDiskCacheStarting) {

  63. try {

  64. mDiskCacheLock.wait();

  65. } catch (InterruptedException e) {}

  66. }

  67. if (mDiskLruCache != null) {

  68. return mDiskLruCache.get(key);

  69. }

  70. }

  71. return null;

  72. }
  73. // 当外部存储器可用时,则在应用指定文件夹中创建一个唯一的子文件夹作为缓存目录

  74. // 而当外部设备不可用时,则使用内置存储器

  75. public static File getDiskCacheDir(Context context, String uniqueName) {

  76. // 检查外部存储器是否可用,如果可用则使用外部存储器的缓存目录

  77. // 否则使用内部存储器的缓存目录

  78. final String cachePath =

  79. Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||

  80. !isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :

  81. context.getCacheDir().getPath();
  82. return new File(cachePath + File.separator + uniqueName);

  83. }

private DiskLruCache mDiskLruCache;
private final Object mDiskCacheLock = new Object();
private boolean mDiskCacheStarting = true;
private static final int DISK_CACHE_SIZE = 1024 * 1024 * 10; // 10MB
private static final String DISK_CACHE_SUBDIR = "thumbnails";

@Override
protected void onCreate(Bundle savedInstanceState) {
...
// 初始化内存缓存
...
// 在后台线程初始化磁盘缓存
File cacheDir = getDiskCacheDir(this, DISK_CACHE_SUBDIR);
new InitDiskCacheTask().execute(cacheDir);
...
}

class InitDiskCacheTask extends AsyncTask<File, Void, Void> {
@Override
protected Void doInBackground(File... params) {
synchronized (mDiskCacheLock) {
File cacheDir = params[0];
mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE);
mDiskCacheStarting = false; // 标识结束初始化
mDiskCacheLock.notifyAll(); // 唤醒等待中的线程
}
return null;
}
}

class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
...
// 在后台解析图片
@Override
protected Bitmap doInBackground(Integer... params) {
final String imageKey = String.valueOf(params[0]);

// 在后台线程中判断图片是否已经存在于磁盘缓存中
Bitmap bitmap = getBitmapFromDiskCache(imageKey);

if (bitmap == null) { // 不存在于磁盘缓存中
// 则正常加载图片
final Bitmap bitmap = decodeSampledBitmapFromResource(
getResources(), params[0], 100, 100));
}

// 将加载出的图片添加到缓存中
addBitmapToCache(imageKey, bitmap);

return bitmap;
}
...
}

public void addBitmapToCache(String key, Bitmap bitmap) {
// 将图片添加到内存缓存中
if (getBitmapFromMemCache(key) == null) {
mMemoryCache.put(key, bitmap);
}

// 同时将图片添加到磁盘缓存中
synchronized (mDiskCacheLock) {
if (mDiskLruCache != null && mDiskLruCache.get(key) == null) {
mDiskLruCache.put(key, bitmap);
}
}
}

public Bitmap getBitmapFromDiskCache(String key) {
synchronized (mDiskCacheLock) {
// 当磁盘缓存正在初始化时,则等待
while (mDiskCacheStarting) {
try {
mDiskCacheLock.wait();
} catch (InterruptedException e) {}
}
if (mDiskLruCache != null) {
return mDiskLruCache.get(key);
}
}
return null;
}

// 当外部存储器可用时,则在应用指定文件夹中创建一个唯一的子文件夹作为缓存目录
// 而当外部设备不可用时,则使用内置存储器
public static File getDiskCacheDir(Context context, String uniqueName) {
// 检查外部存储器是否可用,如果可用则使用外部存储器的缓存目录
// 否则使用内部存储器的缓存目录
final String cachePath =
Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) ||
!isExternalStorageRemovable() ? getExternalCacheDir(context).getPath() :
context.getCacheDir().getPath();

return new File(cachePath + File.separator + uniqueName);
}

注意:既然磁盘缓存的操作涉及到磁盘操作,则此处的所有过程不能在UI线程中执行.同时,这也意味着在磁盘缓存初始化完毕以前是能够被访问的.为了解决这个问题,上述方法中添加了一个锁,这个锁保证了磁盘缓存在初始化完毕之前不会被应用读取.

尽管内存缓存的检查工作可以在UI线程中执行,磁盘缓存的检察工作则必须在后台线程中执行.设计磁盘的操作无论如何不应该在UI线程中执行.当图片加载成功,得到的图片会添加到这两个缓存中去以待使用.

处理配置的更改

当运行时,配置发生了改变,例如屏幕方向的变化.这种变化会使Android系统摧毁并且使用新的配置重建当前正在执行的Activity(有关此方面的更多介绍,请查看Handling
Runtime Changes
).为了使用户有一个顺畅的体验,我们需要避免重新加载所有的图片.

幸运的时,我们有一个不错的内存缓存,这个内存缓存可以通过调用FragmentsetRetainInstance(true)方法保存并且传递到新的Activity中.当Activity被重建后,这个Fragment可以重新依附到新的Activity上,这样我们就可以使用已经存在的内存缓存,快速获取图片并展示在ImageView中.

以下是通过Fragment实现保留LruCache的代码:

view plaincopy to clipboardprint?

  1. private LruCache<String, Bitmap> mMemoryCache;
  2. @Override

  3. protected void onCreate(Bundle savedInstanceState) {

  4. ...

  5. // 得到一个用于保存LruCache的Fragment

  6. RetainFragment retainFragment =

  7. RetainFragment.findOrCreateRetainFragment(getFragmentManager());

  8. // 取出Fragment的LruCache

  9. mMemoryCache = retainFragment.mRetainedCache;

  10. if (mMemoryCache == null) {

  11. // 如果LruCache为空,则原先没有缓存

  12. // 需要新建并初始化一个LruCache

  13. mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {

  14. ... // Initialize cache here as usual

  15. }

  16. // 将新建的LruCache存放到Fragment中

  17. retainFragment.mRetainedCache = mMemoryCache;

  18. }

  19. ...

  20. }
  21. class RetainFragment extends Fragment {

  22. private static final String TAG = "RetainFragment";

  23. public LruCache<String, Bitmap> mRetainedCache;
  24. public RetainFragment() {}
  25. // 新建或者从FragmentManager中得到保存LruCache的Fragment

  26. public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {

  27. // 根据tag从FragmentManager中获取对应的Fragment

  28. RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);

  29. if (fragment == null) {

  30. // 如果Fragment为空,则原先没有该Fragment

  31. // 即表明原先没有LruCache

  32. // 此时需要新建一个Fragment用于存放LruCache

  33. fragment = new RetainFragment();

  34. // 并将Fragment添加到FragmentManager中

  35. fm.beginTransaction().add(fragment, TAG).commit();

  36. }

  37. return fragment;

  38. }
  39. @Override

  40. public void onCreate(Bundle savedInstanceState) {

  41. super.onCreate(savedInstanceState);

  42. // 设置当Activity被重建时,Fragment重新依附到Activity上

  43. setRetainInstance(true);

  44. }

  45. }

private LruCache<String, Bitmap> mMemoryCache;

@Override
protected void onCreate(Bundle savedInstanceState) {
...
// 得到一个用于保存LruCache的Fragment
RetainFragment retainFragment =
RetainFragment.findOrCreateRetainFragment(getFragmentManager());
// 取出Fragment的LruCache
mMemoryCache = retainFragment.mRetainedCache;
if (mMemoryCache == null) {
// 如果LruCache为空,则原先没有缓存
// 需要新建并初始化一个LruCache
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
... // Initialize cache here as usual
}
// 将新建的LruCache存放到Fragment中
retainFragment.mRetainedCache = mMemoryCache;
}
...
}

class RetainFragment extends Fragment {
private static final String TAG = "RetainFragment";
public LruCache<String, Bitmap> mRetainedCache;

public RetainFragment() {}

// 新建或者从FragmentManager中得到保存LruCache的Fragment
public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
// 根据tag从FragmentManager中获取对应的Fragment
RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
if (fragment == null) {
// 如果Fragment为空,则原先没有该Fragment
// 即表明原先没有LruCache
// 此时需要新建一个Fragment用于存放LruCache
fragment = new RetainFragment();
// 并将Fragment添加到FragmentManager中
fm.beginTransaction().add(fragment, TAG).commit();
}
return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置当Activity被重建时,Fragment重新依附到Activity上
setRetainInstance(true);
}
}

为了验证一下效果(是否重新将Fragment依附到Activity上),我们可以旋转一下屏幕.你会发现当我们通过Fragment保存了内存缓存,重建了Activity后重新取出图片几乎没有延时.在内存缓存中没有的图片很可能在磁盘缓存上会有,如果磁盘缓存中也没有,则会正常加载需要的图片.

高效地加载图片(三) 缓存图片,布布扣,bubuko.com

时间: 2024-09-29 16:58:42

高效地加载图片(三) 缓存图片的相关文章

(BUG已修改,最优化)安卓ListView异步加载网络图片与缓存软引用图片,线程池,只加载当前屏之说明

原文:http://blog.csdn.net/java_jh/article/details/20068915 迟点出更新的.这个还有BUG.因为软引应不给力了.2.3之后 前几天的原文有一个线程管理与加载源过多,造成浪费流量的问题.下面对这进下改进的一些说明(红色为新加) 这两天一直在优化这个问题.google也很多种做法.但发现都是比较不全面. 比如: 一些只实现了异步加载,却没有线程池与软引用. 一些是用AsynTast的, 一些有了线程池但加载所有的图片,这样造成具大资源浪费 一些是用

高效地加载图片(四) 管理缓存

除了缓存图片意外,还有一些其他的方式来促进GC的效率和图片的复用.不同的Android系统版本有不同的处理策略.BitmapFun中就包含了这个类,能够使我们高效地构建我们的项目. 为了开始以下教程,我们需要先介绍一下Android系统对Bitmap管理的进化史. 在Android2.2(API level 8)以及更低的版本中,当垃圾被回收时,应用的线程会被停止,这会造成一定程度的延时.在Android 2.3中,加入了并发回收机制,这意味着当Bitmap不再被使用的时候,内存会被很快地回收.

浅谈Android中的异步加载之ListView中图片的缓存及优化三

     隔了很久没写博客,现在必须快速脉动回来.今天我还是接着上一个多线程中的异步加载系列中的最后一个使用异步加载实现ListView中的图片缓存及其优化.具体来说这次是一个综合Demo.但是个人觉得里面还算有点价值的就是里面的图片的缓存的实现.因为老实说它确实能在实际的项目中得到很好的应用.主要学习来源于慕课网中的异步加载学习,来自徐宜生大神的灵感.本次也就是对大神所讲知识的一个总结及一些个人的感受吧. 这次是一个综合的Demo,主要里面涉及到的知识主要有:网络编程.异步加载.JSON解析.

win7(旗舰版)下,OleLoadPicture 加载内存中的图片(MagickGetImageBlob),返回值 &lt; 0

昨天去三哥家,想把拍好的照片缩小一下,我用很久前写的一个软件进行缩小,然后进行一次效果预览,这个时候弹出: Call OleLoadPicture Fail - loadPictureFromMW 奇怪,这个图片批量处理软件我一直在用,在 xp系统下从来没出现过这样的提示,为什么在 win7下无法正常进行图片的效果预览? 更让我接受不了的是,从来没有用户或朋友向我反应这个问题,难道他们都不用 win7? 今天下午问了一个朋友借了台win7的笔记本,通过一下午的测试,解决了这个问题. 之前的代码思

分针网—每日分享: 根据屏幕大小,加载不同大小的图片

引言 今天要介绍的东西,很简单,但是对于前端响应式的时候是个很重要的知识: 我们在用bootstrap这类前端框架时, 虽然页面局部通过media query实现了,页面始终无滚动条,响应式页面. 但是,bootstrap里面的img-responsive类只是通过设置图片100%, 并没有真正的实现在手机上和电脑端加载不同大小的图片. 代码其实很简单 <!DOCTYPE html> <html lang="en"> <head> <meta 

imagepool前端图片加载管理器(JavaScript图片连接池)

前言 imagepool是一款管理图片加载的JS工具,通过imagepool可以控制图片并发加载个数. 对于图片加载,最原始的方式就是直接写个img标签,比如:<img src="图片url" />. 经过不断优化,出现了图片延迟加载方案,这回图片的URL不直接写在src属性中,而是写在某个属性中,比如:<img src="" data-src="图片url" />.这样浏览器就不会自动加载图片,等到一个恰当的时机需要加载

Android利用Volley异步加载数据(JSON和图片)完整示例

Android利用Volley异步加载数据(JSON和图片)完整示例 MainActivity.java package cc.testvolley; import org.json.JSONObject; import android.app.Activity; import android.app.ProgressDialog; import android.graphics.Bitmap; import android.os.Bundle; import android.support.v

asp.net MVC发布iis无法加载css,js和图片

今天真够郁闷的,遇到了在本地能运行的项目到了iis服务器那里就不行了,无法加载css,js和图片,这里说清楚一下先,关于asp.net 的MVC中这样的情况其实不少,但是之前遇到的是在visual studio运行的时候就已经不能加载css和js文件,那种情况一般都是路径的问题,改下页面代码就行,网上教程不少,而这个其实是一个CMS的开源系统.Orchard,国庆实在无聊,就想玩下这个asp.net MVC框架的CMS,而且是微软推荐的开源CMS,提到了就来说说这个吧,和国内的其他CMS对比起来

使用MFC CImage类和GDI+ Image加载并绘制PNG图片

一.使用MFC CImage类加载PNG图片        为了测试CImage绘制PNG图片的效果,我们用截图软件截得一张360的界面,然后使用PhotoShop等工具在图片的周边加上了透明的区域,然后保存成PNG图片文件.CImage首先从文件中加载,即 CImage* m_pImgBk; ...... m_pImgBk = new CImage; m_pImgBk->Load( _T("res\\bk.png")); if ( m_pImgBk->IsNull() )