赞
踩
以问题的形式记录ImageLoader 图片加载器相关知识
问题描述:app有使用ImageLoader加载图片,假如加载某个场景图片A,后来场景换了又产生了一张图片,此时又命名为A,这时候其实图片已经换了,但是显示还是之前的图片?
分析:要解决这个问题就要了解ImageLoader的图片加载机制(参考文章)和实现原理,下面来分析一下这个国民程序员加载图片库。
核心代码
- public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener, FileImageDecoder mFileImageDecoder) {
- this.checkConfiguration();
- ........
-
- // uri为空 展示逻辑
- if(TextUtils.isEmpty(uri)) {
- this.engine.cancelDisplayTaskFor(imageAware);
- listener.onLoadingStarted(uri, imageAware.getWrappedView());
- if(options.shouldShowImageForEmptyUri()) {
- imageAware.setImageDrawable(options.getImageForEmptyUri(this.configuration.resources));
- } else {
- imageAware.setImageDrawable((Drawable)null);
- }
-
- listener.onLoadingComplete(uri, imageAware.getWrappedView(), (Bitmap)null);
- } else {
-
- ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, this.configuration.getMaxImageSize());
- String memoryCacheKey;
- //根据uri生成key
- if(options.isMemoryCacheKeySplitQuestionMark()) {
- String keyUri = StorageUtils.generateKeyUri(uri);
- memoryCacheKey = MemoryCacheUtils.generateKey(TextUtils.isEmpty(keyUri)?uri:keyUri, targetSize);
- } else {
- memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
- }
-
- this.engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
- listener.onLoadingStarted(uri, imageAware.getWrappedView());
- //用此key来从缓存中获取图片
- Bitmap bmp = (Bitmap)this.configuration.memoryCache.get(memoryCacheKey);
- ImageLoadingInfo imageLoadingInfo;
- // 获取到了图片
- if(bmp != null && !bmp.isRecycled()) {
- if(this.configuration.writeLogs) {
- L.d("Load image from memory cache [%s]", new Object[]{memoryCacheKey});
- }
-
- if(options.shouldPostProcess()) {
- imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri));
- ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(this.engine, bmp, imageLoadingInfo, defineHandler(options));
- if(options.isSyncLoading()) {
- displayTask.run();
- } else {
- this.engine.submit(displayTask);
- }
- } else {
- options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
- listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
- }
- } else {
- // 没有获取到,图片展示逻辑
- if(options.shouldShowImageOnLoading()) {//是否设置了加载图片时候的回调
- imageAware.setImageDrawable(options.getImageOnLoading(this.configuration.resources));
- } else if(options.isResetViewBeforeLoading()) {
- imageAware.setImageDrawable((Drawable)null);
- }
-
- imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, this.engine.getLockForUri(uri));
- LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(this.engine, imageLoadingInfo, defineHandler(options), mFileImageDecoder);
- if(options.isSyncLoading()) {
- displayTask.run();
- } else {
- this.engine.submit(displayTask);
- }
- }
-
- }
- }
- }
其大致逻辑是
先缓存,在磁盘,最后没有就网络获取;这里的顺序和代码的逻辑一样的,根据图片uri生成一个key,然后用key去找图片,看看这个key是怎么生成的?
- ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, this.configuration.getMaxImageSize());
- String memoryCacheKey;
- if(options.isMemoryCacheKeySplitQuestionMark()) {
- String keyUri = StorageUtils.generateKeyUri(uri);
- memoryCacheKey = MemoryCacheUtils.generateKey(TextUtils.isEmpty(keyUri)?uri:keyUri, targetSize);
- } else {
- memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
- }
-
-
- public static String generateKey(String imageUri, ImageSize targetSize) {
- return imageUri + "_" + targetSize.getWidth() + "x" + targetSize.getHeight();
- }
关键是generateKey 方法,这里根据uri 和 内部定义的 targetSize来去生成key,这样假如图片的名称没有改变那这样得到同一个key,再回到之前displayImage方法,就会看到ImageLoader会从内存中去加载图片,这时候就不会走下面的逻辑了,导致虽然图片内容变了但是显示的图片还是之前的原因,所以这个问题到此也就分析出来了;
很简单的,既然你是从缓存中获取图片,那我在设置Options 的时候禁用缓存就可以了。
.cacheInMemory(false).cacheOnDisk(false)
同步的概念理解:就是顺序执行,直到图片加载请求结束后,在去执行其他代码;可以百度 “同步网络请求什么意思” ,或者参考一下 同步和异步。
使用场景:假如推送中包含文字信息和图片信息,由于及时性的特性,要求推送产生后,先把文字推送出去(文字信息包含图片的url),并同时上传图片(这个图片上传是耗时的,等图片上传完了后url才会有图片),这样会造成两者的不同步,但app端要同时展示文字和图片?
解决办法,见代码示例:
- // 由于推送文字消息和图片有时间差,当推送过来时就同步加载图片10次,若10次之后还没有加载到,则认为图片有问题
- Bitmap bigBitmap = null;
- for (int i = 0; i < 10; i++) {
- bigBitmap = ImageLoader.getInstance().loadImageSync(largeIconUrl);
- if (bigBitmap != null){
- break;
- }
- LogUtil.debugLog("tag", "for循环: " + i);
- }
-
- if (null != bigBitmap){
- 图片加载出来了,并设置推送大图
- builder.setLargeIcon(bigBitmap);
- builder.setStyle(new Notification.BigPictureStyle().bigPicture(bigBitmap));
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- notification = builder.build();
- } else {
- notification = builder.getNotification();
- }
- notificationManager.notify(xx, notification);
-
- }else {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- notification = builder.build();
- } else {
- notification = builder.getNotification();
- }
- notificationManager.notify(xx, notification);
-
- }
由于,可以得出同步和异步的区别
这个大图获取是在service中实现的,自己验证了一下在actvity中直接用 loadImageSync 会报异常android.os.NetworkOnMainThreadException,所以这个后面继续研究一下?
参考文章 点击链接,可以看看相关配置介绍。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。