彻底掌握如何有效处理高清大图:Android-Universal-Image-Loader框架解析(一)基本使用
彻底掌握如何有效处理高清大图:Android-Universal-Image-Loader框架解析(二)缓存
在UIL框架中,下载,解码和显示三个模块也是比较核心的内容;这篇我们将逐一分析
- 下载模块
在这个模块当中,所有的下载类都实现了ImageDownloader接口,其默认下载类为BaseImageDownloader
BaseImageDownloader主要作用:
他能够从网络,文件系统,app本身资源当中根据图片URL来获得图片的输入流
BaseImageDownloader核心方法
@Override
public InputStream getStream(String imageUri, Object extra) throws IOException {
switch (Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
return getStreamFromNetwork(imageUri, extra);
case FILE:
return getStreamFromFile(imageUri, extra);
case CONTENT:
return getStreamFromContent(imageUri, extra);
case ASSETS:
return getStreamFromAssets(imageUri, extra);
case DRAWABLE:
return getStreamFromDrawable(imageUri, extra);
case UNKNOWN:
default:
return getStreamFromOtherSource(imageUri, extra);
}
}
从getStream方法可知,UIL框架提供解析http,https,file,content,assets,和drawable类型的scheme的文件;(我们这里以解析http和https文件为例)
当一个图片的url的scheme以http或者https为开始的时候,则会调用getStreamFromNetwork来从网络下载图片
protected InputStream getStreamFromNetwork(String imageUri, Object extra) throws IOException {
HttpURLConnection conn = createConnection(imageUri, extra);
int redirectCount = 0;
while (conn.getResponseCode() / 100 == 3 && redirectCount < MAX_REDIRECT_COUNT) {
conn = createConnection(conn.getHeaderField("Location"), extra);
redirectCount++;
}
InputStream imageStream;
try {
imageStream = conn.getInputStream();
} catch (IOException e) {
// Read all data to allow reuse connection (http://bit.ly/1ad35PY)
IoUtils.readAndCloseStream(conn.getErrorStream());
throw e;
}
if (!shouldBeProcessed(conn)) {
IoUtils.closeSilently(imageStream);
throw new IOException("Image request failed with response code " + conn.getResponseCode());
}
return new ContentLengthInputStream(new BufferedInputStream(imageStream, BUFFER_SIZE), conn.getContentLength());
}
这个方法比较简单,就是通过HttpURLConnection的方式获得url的输入流,同时在下载的时候,我们应该注意到URL框架默认的重定向次数为5次,同时也可以记住一个小知识点,如果服务端返回的状态码以3开头,则均表示为重定向
- 解码模块
当通过下载模块将图片转换为输入流之后,我们就需要通过解码模块来解码这个输入流了
在解码模块当中,ImageDecoder是所有解码器的接口,BaseImageDecoder是默认的解码器,而ImageDecodingInfo里面则是需要解码的信息
ImageDecodingInfo主要作用
封装解码信息,里面主要包含图片的url信息,图片缩放类型,另外里面有一个重要的成员,即Options decodingOptions;
Options类,通常用于设置一些加载到内存中的图片的参数,这个在图片加载的时候都会使用这个类来控制加载的图片
Options.inJustDecodeBounds 设置为true,只会返回图片尺寸,不加载图片到内存
Options.inSampleSize 这个值是一个int,当它小于1的时候,将会被当做1处理,如果大于1,那么就会按照比例(1 / inSampleSize)缩小bitmap的宽和高、降低分辨率,大于1时这个值将会被处置为2的倍数。例如,width=100,height=100,inSampleSize=2,那么就会将bitmap处理为,width=50,height=50,宽高降为1 / 2,像素数降为1 / 4
Options.inScaled设置这个Bitmap是否可以被缩放,默认值是true,表示可以被缩放。
Options.inPreferQualityOverSpeed这个值表示是否在解码时图片有更高的品质,仅用于JPEG格式。如果设置为true,则图片会有更高的品质,但是会解码速度会很慢(API24之后被废弃,即24之后,默认解图为高质量,24之前如果设置为true,)
Options.outWidth和Options.outHeight表示这个Bitmap的宽和高,一般和inJustDecodeBounds一起使用来获得Bitmap的宽高,但是不加载到内存
Options.inMutable如果设置为true,则decoder返回的是可变的Bitmap,这可以用来对Bitmap施加一些效果
BaseImageDecoder主要作用
通过ImageDecodingInfo来获得带解码的图片的scheme,根据scheme去获得对应图片的输入流,BaseImageDecoder根据得到的输入流来进行解码
BaseImageDecoder核心方法
public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException {
Bitmap decodedBitmap;
ImageFileInfo imageInfo;
InputStream imageStream = getImageStream(decodingInfo);
if (imageStream == null) {
L.e(ERROR_NO_IMAGE_STREAM, decodingInfo.getImageKey());
return null;
}
try {
imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo);
imageStream = resetStream(imageStream, decodingInfo);
Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo);
decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions);
} finally {
IoUtils.closeSilently(imageStream);
}
if (decodedBitmap == null) {
L.e(ERROR_CANT_DECODE_IMAGE, decodingInfo.getImageKey());
} else {
decodedBitmap = considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation,
imageInfo.exif.flipHorizontal);
}
return decodedBitmap;
}
第5行,下载模块BaseImageDownloader根据scheme获得图片的输入流
第14行,把当前图片流根据Options的参数设置来进行解码,返回Bitmap
- 显示模块
当图片经过下载,然后被解码生成新的bitmap之后,就需要通过显示模块显示在上层界面上了
显示模块的基础接口是BitmapDisplayer,各个显示模块均实现了BitmapDisplayer接口,主要作用就是针对bitmap做修改,返回一个新的bitmap,这样就可以显示在上层界面上了,此处以FadeInBitmapDisplayer为例
FadeInBitmapDisplayer主要作用
imageview显示图片的形式是淡入模式
FadeInBitmapDisplayer核心代码
@Override
public void display(Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom) {
imageAware.setImageBitmap(bitmap);
if ((animateFromNetwork && loadedFrom == LoadedFrom.NETWORK) ||
(animateFromDisk && loadedFrom == LoadedFrom.DISC_CACHE) ||
(animateFromMemory && loadedFrom == LoadedFrom.MEMORY_CACHE)) {
animate(imageAware.getWrappedView(), durationMillis);
}
}
/**
* Animates {@link ImageView} with "fade-in" effect
*
* @param imageView {@link ImageView} which display image in
* @param durationMillis The length of the animation in milliseconds
*/
public static void animate(View imageView, int durationMillis) {
if (imageView != null) {
AlphaAnimation fadeImage = new AlphaAnimation(0, 1);
fadeImage.setDuration(durationMillis);
fadeImage.setInterpolator(new DecelerateInterpolator());
imageView.startAnimation(fadeImage);
}
}
代码比较简单,就是设置imageView的显示图片,并展示一个AlphaAnimation动画