这里给大家分享一些Android异步加载全解析之引入一级缓存(共含6篇),供大家参考。同时,但愿您也能像本文投稿人“TenderPinecone”一样,积极向本站投稿分享好文章。
通过对图像的缩放,我们做到了对大图的异步加载优化,但是现在的App不仅是高清大图,更是高清多图,动不动就是图文混排,以图代文,如果这些图片都加载到内存中,必定会OOM,因此,在用户浏览完图像后,应当立即将这些废弃的图像回收,但是,这又带来了另一个问题,也就是当用户在浏览完一次图片后,如果还要返回去再进行重新浏览,那么这些回收掉的图像又要重新进行加载,保不准就要那些无聊到 的人在那一边看你回收GC,一边看你重新加载。这两件事情,肯定是互相矛盾的,也是影响性能的一个很重要的原因。
内存缓存LruCache所使用的内存缓存大小是由开发者决定的,开发者需要根据图像的使用率、分辨率、访问频率、设备性能等很多因素进行考虑。这个平衡点经常需要很多经验和测试来决定。使用LruCache非常简单:
private LruCache
首先,我们需要声明LruCache,接着,通过LruCache的构造方法创建缓存对象,并为其分配cacheSize,这个cacheSize通常我们需要通过Runtime来获取,获取当前系统分给App的可用内存,并将这些内存的一部分用做LruCache缓存。LruCache中必须重写sizeOf方法,通过这个方法,LruCache可以获取每个缓存对象的大小,子类必须重写,因为默认的LruCache获取的是缓存的个数。。。 。 最后,我们提供两个方法getBitmapFromMemoryCaches和addBitmapToMemoryCaches分别用来获取和增加内存缓存到LruCache。 等等,我们好像还没写释放内存的方法,对,不用你写了,Lru算法可以保证cacheSize不会OOM,一旦超过这个大小,GC就会回收时间最长的对象,释放空间。
针对这样一个非常需要找到一个彼此平衡点的问题,Google提供了一套内存缓存技术。内存缓存技术对那些大量占用应用程序宝贵内存的图片提供了快速访问的方法。其中最核心的类是LruCache 。这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。LruCache 是在support-v4中才引入的,在引入LruCache 之前,Google建议的是使用软引用或弱引用 (SoftReference or WeakReference)来进行内存缓存。但是从Android 2.3开始,GC算法修改,软引用与弱引用同样会优先被GC回收,所以这种方法也就没有太高的使用价值了,现在网上很多还在继续使用SoftReference 和WeakReference的文章,大多都是过时的文章,建议大家跟上党的步伐,与时俱进。
OK,在了解了关于缓存的基础信息后,我们回到现在这个例子,想想怎么利用缓存来进行异步处理的优化。首先,ListView、GridView这些娇生惯养的玩意儿,碰不得摔不得,更不能在它滚的开心的时候,你还在后面拼命玩加载。所以,第一个重点,滚的时候就让它开心的滚,滚完了再开始加载。
滚完再加载
要实现这一点,我们可以通过给Adapter增加AbsListView.OnScrollListener接口来实现。 当然,还有一点需要注意,第一次初始化的时候,一定要手动来加载图片,不然系统判断你没滚,只能调用onScroll方法,不会调用onScrollStateChanged方法。而且我们也需要在onScroll方法中来不断获取可见的Item。特别要注意的是visibleItemCount,只要大于0的时候,才认为是开始显示图片了。
@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == SCROLL_STATE_IDLE) { mImageLoader.loadImages(mStart, mEnd); } else { mImageLoader.cancelAllTasks; }}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mStart = firstVisibleItem; mEnd = firstVisibleItem + visibleItemCount; if (mFirstFlag && visibleItemCount >0) { mImageLoader.loadImages(mStart, mEnd); mFirstFlag = false; }}
加载显示的项目
加载数据的时候,获取第一个能显示的Item和最后一个可见的Item,只加载这一部分。所以我们创建一个方法——loadImages(int start, int end)。这个方法用来加载从start到end之间的Item数据。 加载的时候,先从内存缓存中去取,如果有,那说明最近已经加载过了,那直接加载就好了,如果没有取到,那就开启synctask去下载。
public void loadImages(int start, int end) { for (int i = start; i < end; i++) { String url = Images.IMAGE_URLS[i]; Bitmap bitmap = getBitmapFromMemoryCaches(url); if (bitmap == null) {ASyncDownloadImage task = new ASyncDownloadImage(url);mTasks.add(task);task.execute(url); } else {ImageView imageView = (ImageView) mListView.findViewWithTag(url);imageView.setImageBitmap(bitmap); } }}
这里我们在设置图片的时候,直接通过findViewWithTag,通过url来找到相应的Imageview,这里与之前不同是因为我们这里是按照start到end来进行加载,直接从ListView对象中获取对应的Imageview比较简单。
下载与Asynctask
下载依然是使用老方法:
private static Bitmap getBitmapFromUrl(String urlString) { Bitmap bitmap; InputStream is = null; try { URL url = new URL(urlString); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); is = new BufferedInputStream(conn.getInputStream()); bitmap = BitmapFactory.decodeStream(is); conn.disconnect(); return bitmap; } catch (Exception e) { e.printStackTrace(); } finally { try {if (is != null) is.close(); } catch (IOException e) { } } return null;}
Asynctask也与之前基本类似:
class ASyncDownloadImage extends AsyncTask
唯一不同的是,我们在下载好图像之后,会将图像加载到Lrucache。
组装
OK,万事具备,准备刷代码。在刷之前,我们先来重新整理下思路,首先,在Adapter中,一加载ListView,就开始下载显示范围内的Item的图像,这时候缓存中当然没有,所以都去下载了,下完了就显示在Item中,并缓存起来,如果还没下完,你就迫不及待的滚起来了,那么立即取消所有task,让ListView欢快的滚,滚完之后,继续加载。 OK,该讲的都讲了,下面我们开始刷代码了,一切尽在不言中,只有代码最懂你。
package com.imooc.listviewacyncloader;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.AsyncTask;import android.util.LruCache;import android.widget.ImageView;import android.widget.ListView;import java.io.BufferedInputStream;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.URL;import java.util.HashSet;import java.util.Set;public class ImageLoaderWithCaches { private Set mTasks; private LruCache
下面是Adapter的代码:
package com.imooc.listviewacyncloader;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.ListView;import java.util.List;public class MyAdapterUseCaches extends BaseAdapter implements AbsListView.OnScrollListener { private LayoutInflater mInflater; private List
是不是非常简单,现在引入缓存了,下载过的图片会暂时保存在内存中,妈妈再也不用担心你OOM啦,
我们下拉试试,下载完的图片再次出现也可以马上加载了,除非滑动太多导致GC。
可以就看见,我们的这次利用缓存进行加载有这样几个特点: 1、初始化的时候加载 2、滑动的时候才加载 3、加载的内容暂存缓存中 4、只加载显示的区域
项目地址:github.com/nostra13/Android-Universal-Image-Loader UIL(Universal-Image-Loader)异步图像加载、缓存和显示.这个图片异步加载并缓存的类已经被很多开发者所使用,是最常用的几个开源库之一,主流的应用,随便反编译几个火的项目,都可以见到它的身影, 同类类库(Picasso),尽管Picasso拥有更好的API,但其缺乏自定义。而使用UIL构建器几乎可以配置所有(其中最重要的就是在抓取和缓存大型图片时,Picasso会失败)。
特点: 多线程加载图像Multithread image loading (async or sync)宽泛的自定义配置Wide customization of ImageLoader's configuration (thread executors, downloader, decoder, memory and disk cache, display image options, etc.)Many customization options for every display image call (stub images, caching switch, decoding options, Bitmap processing and displaying, etc.)图像缓存Image caching in memory and/or on disk (device's file system or SD card)加载过程监听Listening loading process (including downloading progress) 简单描述一下这个项目的结构:每一个图片的加载和显示任务都运行在独立的线程中,除非这个图片缓存在内存中,这种情况下图片会立即显示。如果需要的图片缓存在本地,他们会开启一个独立的线程队列。如果在缓存中没有正确的图片,任务线程会从线程池中获取,因此,快速显示缓存图片时不会有明显的障碍。
由于源码中不管是loadImageSync还是loadImage最后都会通过displayImage来加载。那我们看看其流程:
安装:
maven:
Gradle:
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.3'
添加网络和SD卡权限:
由于是使用过程中会图片获取要通过网络,并且有缓存设置,所以这2个权限必须要有。
预配置Application or Activity class (before the first usage of ImageLoader)
// Create global configuration and initialize ImageLoader with this config ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this)....build(); ImageLoader.getInstance().init(config);