转载请注明出处:http://blog.csdn.net/u011733020
前言:
在Android开发中,对于图片的加载可以说是个老生常谈的问题了,图片加载是一个比较坑的地方,处理不好,会有各种奇怪的问题,比如 加载导致界面卡顿,程序crash。
因此 如何高效的加载大量图片,以及如何加载大分辨率的图片到内存,是我们想要开发一款优质app时不得不去面对与解决的问题。
通常开发中,我们只有两种选择:① 使用开源框架 ②自己去实现处理图片的加载与缓存。
通常一开始让我们自己去写,我们会无从下手,因此先去分析一下开源的思路,对我们的成长很有必要。
目前使用频率较高的图片缓存框架有 Universal-Image-Loader、android-Volley、Picasso、Fresco和Glide五大Android开源组件。
首先排除android-Volley 孰优孰劣,后面再去验证,剩下的四种对于 图片加载缓存的思想,从大方向上应该是类似的
而Android-Universal-Image-Loader(UIL for short)作为一款比较经典的框架,从早期到现在一直都比较常见,这里就拿UIL来看一下它对图片处理的思想,然后设置写一下自己的imagloader,本文首先介绍一下UIL的基本信息。
正文:
在本文中先介绍一下UIL在Github上的使用介绍。
UIL 在github上的地址https://github.com/nostra13/Android-Universal-Image-Loader 遗憾的是作者在2015.11月已经声明不在维护更新了。
UIL的介绍:Powerful and flexible library for loading, caching and displaying images on Android(高效灵活的加载、缓存以及展示图片)。
下面看一下具体怎么用
1. Include library
Manual:
- Download JAR
- Put the JAR in the libs subfolder of your Android project
or
Maven dependency:
<dependency>
<groupId>com.nostra13.universalimageloader</groupId>
<artifactId>universal-image-loader</artifactId>
<version>1.9.5</version>
</dependency>
or
Gradle dependency:
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
2. Android Manifest
<manifest>
<!-- Include following permission if you load images from Internet -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- Include following permission if you want to cache images on SD card -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
3. Application or Activity class (before the first usage of ImageLoader)
public class MyActivity extends Activity {
@Override
public void onCreate() {
super.onCreate();
// Create global configuration and initialize ImageLoader with this config
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this)
...
.build();
ImageLoader.getInstance().init(config);
...
}
}
ImageLoader Configuration (ImageLoaderConfiguration
) is global for application. You should set it once.
All options in Configuration builder are optional. Use only those you really want to customize.
See default values for config options in Java docs for every option.
// DON'T COPY THIS CODE TO YOUR PROJECT! This is just example of ALL options using.
// See the sample project how to use ImageLoader correctly.
File cacheDir = StorageUtils.getCacheDirectory(context);
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800) // default = device screen dimensions
.diskCacheExtraOptions(480, 800, null)
.taskExecutor(...)
.taskExecutorForCachedImages(...)
.threadPoolSize(3) // default
.threadPriority(Thread.NORM_PRIORITY - 2) // default
.tasksProcessingOrder(QueueProcessingType.FIFO) // default
.denyCacheImageMultipleSizesInMemory()
.memoryCache(new LruMemoryCache(2 * 1024 * 1024))
.memoryCacheSize(2 * 1024 * 1024)
.memoryCacheSizePercentage(13) // default
.diskCache(new UnlimitedDiskCache(cacheDir)) // default
.diskCacheSize(50 * 1024 * 1024)
.diskCacheFileCount(100)
.diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default
.imageDownloader(new BaseImageDownloader(context)) // default
.imageDecoder(new BaseImageDecoder()) // default
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
.writeDebugLogs()
.build();
Display Options (DisplayImageOptions
) are local for every display task (ImageLoader.displayImage(...)
).
Display Options can be applied to every display task (ImageLoader.displayImage(...)
call).
Note: If Display Options wasn't passed to ImageLoader.displayImage(...)
method then default Display Options from configuration (ImageLoaderConfiguration.defaultDisplayImageOptions(...)
) will be used.
// DON'T COPY THIS CODE TO YOUR PROJECT! This is just example of ALL options using.
// See the sample project how to use ImageLoader correctly.
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub) // resource or drawable
.showImageForEmptyUri(R.drawable.ic_empty) // resource or drawable
.showImageOnFail(R.drawable.ic_error) // resource or drawable
.resetViewBeforeLoading(false) // default
.delayBeforeLoading(1000)
.cacheInMemory(false) // default
.cacheOnDisk(false) // default
.preProcessor(...)
.postProcessor(...)
.extraForDownloader(...)
.considerExifParams(false) // default
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default
.bitmapConfig(Bitmap.Config.ARGB_8888) // default
.decodingOptions(...)
.displayer(new SimpleBitmapDisplayer()) // default
.handler(new Handler()) // default
.build();
Caching is NOT enabled by default. If you want loaded images to be cached in memory and/or on disk then you should enable caching in DisplayImageOptions this way:
// Create default options which will be used for every
// displayImage(...) call if no options will be passed to this method
DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()
...
.cacheInMemory(true)
.cacheOnDisk(true)
...
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
...
.defaultDisplayImageOptions(defaultOptions)
...
.build();
ImageLoader.getInstance().init(config); // Do it on Application start
// Then later, when you want to display image
ImageLoader.getInstance().displayImage(imageUrl, imageView); // Default options will be used
or this way:
DisplayImageOptions options = new DisplayImageOptions.Builder()
...
.cacheInMemory(true)
.cacheOnDisk(true)
...
.build();
ImageLoader.getInstance().displayImage(imageUrl, imageView, options); // Incoming options will be used
If you enabled disk caching then UIL try to cache images on external storage (/sdcard/Android/data/[package_name]/cache). If external storage is not available then images are cached on device's filesystem. To provide caching on external storage (SD card) add following permission to AndroidManifest.xml:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" />
How UIL define Bitmap size needed for exact ImageView? It searches defined parameters:
- Get actual measured width and height of ImageView
- Get
android:layout_width
and android:layout_height
parameters
- Get
android:maxWidth
and/or android:maxHeight
parameters
- Get maximum width and/or height parameters from configuration (
memoryCacheExtraOptions(int, int)
option)
- Get width and/or height of device screen
So try to set android:layout_width
|android:layout_height
orandroid:maxWidth
|android:maxHeight
parameters for ImageView if you know approximate maximum size of it. It will help correctly compute Bitmap size needed for this view and save memory.
If you often got OutOfMemoryError in your app using Universal Image Loader then:
- Disable caching in memory. If OOM is still occurs then it seems your app has a memory leak. Use MemoryAnalyzer to detect it. Otherwise try the following steps (all of them or several):
- Reduce thread pool size in configuration (
.threadPoolSize(...)
). 1 - 5 is recommended.
- Use
.bitmapConfig(Bitmap.Config.RGB_565)
in display options. Bitmaps in RGB_565 consume 2 times less memory than in ARGB_8888.
- Use
.imageScaleType(ImageScaleType.EXACTLY)
- Use
.diskCacheExtraOptions(480, 320, null)
in configuration
For memory cache configuration (ImageLoaderConfiguration.memoryCache(...)
) you can use already prepared implementations.
- Cache using only strong references:
LruMemoryCache
(Least recently used bitmap is deleted when cache size limit is exceeded) - Used by default
- Caches using weak and strong references:
UsingFreqLimitedMemoryCache
(Least frequently used bitmap is deleted when cache size limit is exceeded)
LRULimitedMemoryCache
(Least recently used bitmap is deleted when cache size limit is exceeded)
FIFOLimitedMemoryCache
(FIFO rule is used for deletion when cache size limit is exceeded)
LargestLimitedMemoryCache
(The largest bitmap is deleted when cache size limit is exceeded)
LimitedAgeMemoryCache
(Decorator. Cached object is deleted when its age exceeds defined value)
- Cache using only weak references:
WeakMemoryCache
(Unlimited cache)
For disk cache configuration (ImageLoaderConfiguration.diskCache(...)
) you can use already prepared implementations:
UnlimitedDiscCache
(The fastest cache, doesn't limit cache size) - Used by default
LruDiskCache
(Cache limited by total cache size and/or by file count. If cache size exceeds specified limit then least-recently used file will be deleted)
LimitedAgeDiscCache
(Size-unlimited cache with limited files' lifetime. If age of cached file exceeds defined limit then it will be deleted from cache.)
NOTE: UnlimitedDiscCache is pretty faster than other limited disk cache implementations.
To display bitmap (DisplayImageOptions.displayer(...)
) you can use already prepared implementations:
RoundedBitmapDisplayer
(Displays bitmap with rounded corners)
FadeInBitmapDisplayer
(Displays image with "fade in" animation)
To avoid list (grid, ...) scrolling lags you can use PauseOnScrollListener
:
boolean pauseOnScroll = false; // or true
boolean pauseOnFling = true; // or false
PauseOnScrollListener listener = new PauseOnScrollListener(imageLoader, pauseOnScroll, pauseOnFling);
listView.setOnScrollListener(listener);
If you see in logs some strange supplement at the end of image URL (e.g.http://anysite.com/images/image.png_230x460
) then it doesn't mean this URL is used in requests. This is just "URL + target size", also this is key for Bitmap in memory cache. This postfix (_230x460
) is NOT used in requests.
ImageLoader always keeps aspect ratio of images.
以上都是UIL在Github的 readme 中提到的使用,以及注意事项,当遇到问题是,先在useful info中看是否能找到答案,然后在去找其他解决方案。
将UIL添加到我们的工程中只需要以下三步即可:
1.下载lib 导入到目标工程。
2.manifest 文件中添加权限。
3.在application 中进行UIL的初始化配置。
然后通过 displayImage(String uri, ImageView imageView) 进行加载图片使用。
UIL特点介绍:
可以通过配置显示圆角图片。
支持多种网络情况下的 加载机制。
只需要传递一个url 即可(网络、本地都可以)。
将要显示的图片url 与View绑定,避免了图片错位这种现象。
使用线程池管理任务线程:既保证加载效率,也保证了优化app性能。
设置本地缓存路径,方便后期查看管理缓存文件(清空本地缓存)。
加载高分辨率的图片,自动改变bitmap分辨率,减少内存占用防止oom。
支持特殊情况比如滑动过程中暂停加载,停止滑动后,在加载,保证了加载当前页面的效率。
可以添加下载过程的监听,可以设置下载中的默认图片背景,下载完成后,可以选择显示图片的动画。
实际项目中使用:
下面是在项目中使用的具体介绍:
1.在Application中进行init:
public static void initImageLoader(Context context) {
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
context)
.threadPriority(Thread.NORM_PRIORITY + 2)
.denyCacheImageMultipleSizesInMemory()
.threadPoolSize(10)
.discCacheFileNameGenerator(new Md5FileNameGenerator())
.tasksProcessingOrder(QueueProcessingType.LIFO)
.diskCache(
new UnlimitedDiskCache(new File(ApplicationData
.getPicDir())))
.memoryCache(new LRULimitedMemoryCache(2 * 1024 * 1024))
.build();
ImageLoader.getInstance().init(config);
}
2.在要显示图片的地方,比如ListView、GridView的Adapter的getView() 方法中:
imageLoader.displayImage(url, shower, roundShapeOption) ;
并且可以动态传入 需要的option:
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.trans_pic)
.showImageForEmptyUri(R.drawable.trans_pic)
.showImageOnFail(R.drawable.trans_pic)
.delayBeforeLoading(IMG_LOAD_DELAY)
.cacheInMemory(true)
.cacheOnDisc(true)
.bitmapConfig(Bitmap.Config.RGB_565) //设置图片的解码类型
.build();
如何在低内存手机上减少OOM的概率:
在低内存的手机上建议以下配置:
对ImageLoaderConfiguration 参数的设置:
减少配置之中线程池的大小(threadPoolSize)
对DisplayImageOptions 参数设置:
imageScaleType(ImageScaleType.IN_SAMPLE_INT)或者 try.imageScaleType(ImageScaleType.EXACTLY)。
.bitmapConfig(Bitmap.Config.RGB_565) //默认是ARGB_8888,使用RGB_565会比使用ARGB_8888少消耗2倍的内。
避免使用RoundedBitmapDisplayer.他会创建新的ARGB_8888格式的Bitmap对象。
使用.memoryCache(new WeakMemoryCache()),不要使用.cacheInMemory()。
根据url 的来源:
http://site.com/image.png // from Web
file:///mnt/sdcard/image.png // from SD card
file:///mnt/sdcard/video.mp4 // from SD card (video thumbnail)
content://media/external/images/media/13 // from content provider
content://media/external/video/media/13 // from content provider (video thumbnail)
assets://image.png // from assets
drawable:// + R.drawable.img // from drawables (non-9patch images)
file:///storage/emulated/0/UIL/Document/pics/aaa.jpg
手机版本不一致,可能也不一样 ,具体还是要自己灵活区分。
更多配置参考:http://www.yq1012.com/android/2053.html 。
本文主要介绍 UIL的基本接入以及 使用配置,有关加载过程,缓存过程,请看下一篇 Android-Universal-Image-Loader (图片异步加载缓存库)的源码解读