内存缓存-LruCache 与 压缩图片
来源于郭大神:http://blog.csdn.net/guolin_blog/article/details/9316683
LruCache这个类非常适合用来缓存图片,它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除.与它对应的是DiskLruCache(磁盘缓存)
我们在编写Android程序的时候经常要用到许多图片,不同图片总是会有不同的形状、不同的大小,但在大多数情况下,这些图片都会大于我们程序所需要的大小。比如说系统图片库里展示的图片大都是用手机摄像头拍出来的,这些图片的分辨率会比我们手机屏幕的分辨率高得多。大家应该知道,我们编写的应用程序都是有一定内存限制的,程序占用了过高的内存就容易出现OOM(OutOfMemory)异常。我们可以通过下面的代码看出每个应用程序最高可用内存是多少
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); System.out.println("Max memory is " + maxMemory + "KB");//模拟器输出:16384kB
因此在展示高分辨率图片的时候,最好先将图片进行压缩。压缩后的图片大小应该和用来展示它的控件大小相近,在一个很小的ImageView上显示一张超大的图片不会带来任何视觉上的好处,但却会占用我们相当多宝贵的内存,而且在性能上还可能会带来负面影响。下面我们就来看一看,如何对一张大图片进行适当的压缩,让它能够以最佳大小显示的同时,还能防止OOM的出现。
BitmapFactory这个类提供了多个解析方法(decodeByteArray, decodeFile, decodeResource等)用于创建Bitmap对象,我们应该根据图片的来源选择合适的方法。比如SD卡中的图片可以使用decodeFile方法,网络上的图片可以使用decodeStream方法,资源文件中的图片可以使用decodeResource方法。这些方法会尝试为已经构建的bitmap分配内存,这时就会很容易导致OOM出现。为此每一种解析方法都提供了一个可选的BitmapFactory.Options参数,将这个参数的inJustDecodeBounds属性设置为true就可以让解析方法禁止为bitmap分配内存,返回值也不再是一个Bitmap对象,而是null。虽然Bitmap是null了,但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被赋值。这个技巧让我们可以在加载图片之前就获取到图片的长宽值和MIME类型,从而根据情况对图片进行压缩。如下代码所示:
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true;//解析方法禁止为bitmap分配内存 BitmapFactory.decodeResource(getResources(), R.id.myimage, options); int imageHeight = options.outHeight; int imageWidth = options.outWidth; String imageType = options.outMimeType;//类型值(不同数字不同的含义)
并没有一个指定的缓存大小可以满足所有的应用程序,这是由你决定的。你应该去分析程序内存的使用情况,然后制定出一个合适的解决方案。一个太小的缓存空间,有可能造成图片频繁地被释放和重新加载,这并没有好处。而一个太大的缓存空间,则有可能还是会引起 java.lang.OutOfMemory 的异常。
解决OOM异常的方法1,压缩图片:
/** 内存缓存:程序占用了过高的内存就容易出现OOM(OutOfMemory)异常,解决OOM异常的两种方法: 1.处理图片的大小,进行必要的压缩,宽高等比例压缩 2.使用图片还催技术 */ //方法1 public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); int maxMemory = (int) (Runtime.getRuntime().maxMemory() /1024 /1024);//获取应用的最大可用内存 Log.i("TAG", "该应用的最大可用内存为:" + maxMemory + "M");//genymotion 64M ImageView img= (ImageView) findViewById(R.id.imageView); Bitmap bitmap = decodeSampledBitmapFromResource(getResources(), R.drawable.a, 100, 100); img.setImageBitmap(bitmap); } /** * 解码imageVIew为想要的尺寸, * 在一个很小的ImageView上显示一张超大的图片不会带来任何视觉上的好处, * 但却会占用我们相当多宝贵的内存,而且在性能上还可能会带来负面影响。 * @param res 资源对象 * @param resId 图片的id.这里使用的是资源id,对于流,file的图片也是可以的 * @param reqWidth 需要的图片宽度 * @param reqHeight 需要的图片高度 * @return bitmap */ private Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { BitmapFactory.Options options=new BitmapFactory.Options(); options.inJustDecodeBounds=true;//只解码图片大小 BitmapFactory.decodeResource(res, resId, options);//这里得到的bitmap为null options.inSampleSize=calculateInSampleSize(options, reqWidth, reqHeight); //使用获取的比例值再次解析图片,设置为false.返回的bitmap才不会为空 options.inJustDecodeBounds=false; return BitmapFactory.decodeResource(res,resId,options); } /** * 根据图片的实际尺寸与需要的尺寸进行比较计算 * @param options 包含实际图片的宽高 * @param reqWidth 请求的图片宽度 * @param reqHeight 需要的图片高度 * @return 长宽等比例压缩这个值, * 比如我们有一张2048*1536像素的图片,将inSampleSize的值设置为4,就可以把这张图片压缩成512*384像素 */ private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { //获取来源图片的宽高 int height=options.outHeight; int width=options.outWidth; int inSampleSize=1;//默认为1 不压缩图片 if(height>reqHeight || width>reqWidth){ //计算出实际宽高和目标宽高的比率 round代表四舍五入 int heightRatio=Math.round((float) height / (float) reqHeight); int widthRatio=Math.round((float) width / (float) reqWidth); Log.i("tag","实际/目标,高:"+heightRatio+" 宽:"+widthRatio); // 选择宽和高中最小的比率作为inSampleSize的值,这样可以保证最终图片的宽和高,一定都会大于等于目标的宽和高。 inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } }
说明:这里压缩的图片结果是大于或者等于目标的尺寸,图方便这里使用的图片为工程里的资源图片,如果是流,或者的文件,改下个别方法就行
下面是一个使用 LruCache 来缓存图片的例子:
主Activity,MainActivity:
/** 内存缓存:程序占用了过高的内存就容易出现OOM(OutOfMemory)异常,解决OOM异常的两种方法: 1.处理图片的大小,进行必要的压缩,宽高等比例压缩 2.使用图片还催技术 */ //方法2 public class MainActivity extends AppCompatActivity { //图片地址 String[] imgs=new String[]{ "http://a.hiphotos.baidu.com/image/h%3D360/sign=c429bad21dd8bc3ed90800ccb28aa6c8/e7cd7b899e510fb3a78c787fdd33c895d0430c44.jpg", "http://f.hiphotos.baidu.com/image/h%3D360/sign=5ec76a78d8c451dae9f60aed86fd52a5/dbb44aed2e738bd4f60e4017a48b87d6277ff9ed.jpg", "http://b.hiphotos.baidu.com/image/h%3D360/sign=51650b06b83eb1355bc7b1bd961fa8cb/7a899e510fb30f2493c8cbedcc95d143ac4b0389.jpg", "http://h.hiphotos.baidu.com/image/h%3D360/sign=2c82459da31ea8d395227202a70b30cf/43a7d933c895d143b233160576f082025aaf074a.jpg", "http://e.hiphotos.baidu.com/image/h%3D360/sign=ea96ce4c0e7b020813c939e752d8f25f/14ce36d3d539b600be63e95eed50352ac75cb7ae.jpg", "http://h.hiphotos.baidu.com/image/h%3D360/sign=e6bd967ff203738dc14a0a248319b073/08f790529822720e534f0b257fcb0a46f31fab68.jpg", "http://g.hiphotos.baidu.com/image/h%3D360/sign=a3ab0d059758d109dbe3afb4e159ccd0/b7fd5266d0160924d5e7cc60d10735fae7cd348e.jpg", "http://b.hiphotos.baidu.com/image/h%3D360/sign=78d136c1d5a20cf45990f8d946084b0c/9d82d158ccbf6c81a1599daeb93eb13533fa400d.jpg", "http://e.hiphotos.baidu.com/image/h%3D360/sign=8c7b592db8a1cd111ab674268913c8b0/b219ebc4b74543a9f68147d11c178a82b9011410.jpg", "http://c.hiphotos.baidu.com/image/h%3D360/sign=86a67aee8fb1cb1321693a15ed5456da/1ad5ad6eddc451da33c51deab4fd5266d01632fb.jpg", "http://a.hiphotos.baidu.com/image/h%3D360/sign=e7b5381941a7d933a0a8e2759d4bd194/6f061d950a7b020893f23dec61d9f2d3572cc880.jpg", "http://c.hiphotos.baidu.com/image/h%3D360/sign=02a96a298418367ab28979db1e728b68/0b46f21fbe096b6344dcf9500e338744eaf8aced.jpg", "http://h.hiphotos.baidu.com/image/h%3D200/sign=a8af76d39f16fdfac76cc1ee848e8cea/738b4710b912c8fccc8e7b35fe039245d78821cc.jpg", "http://h.hiphotos.baidu.com/image/h%3D360/sign=5067d3011d30e924d0a49a377c096e66/242dd42a2834349b5454a08ccbea15ce36d3be6d.jpg", "http://b.hiphotos.baidu.com/image/h%3D360/sign=aec7700940a7d933a0a8e2759d4ad194/6f061d950a7b0208da8075fc60d9f2d3572cc866.jpg", "http://b.hiphotos.baidu.com/image/h%3D360/sign=11705f58a918972bbc3a06ccd6cc7b9d/267f9e2f07082838de56bf58ba99a9014c08f127.jpg", "http://e.hiphotos.baidu.com/image/h%3D360/sign=0d5bbf79952bd4075dc7d5fb4b889e9c/9f2f070828381f30ed5bacf8ab014c086e06f024.jpg", "http://e.hiphotos.baidu.com/image/h%3D200/sign=a45f36fa013b5bb5a1d727fe06d2d523/cf1b9d16fdfaaf517fb5b6ee8e5494eef01f7a08.jpg", "http://h.hiphotos.baidu.com/image/h%3D200/sign=f541a648b0de9c82b965fe8f5c8180d2/0824ab18972bd40752e3918c78899e510fb3099e.jpg", "http://a.hiphotos.baidu.com/image/h%3D360/sign=349a818ff21f3a2945c8d3c8a924bce3/fd039245d688d43f3f5ab1a37f1ed21b0ff43beb.jpg", "http://b.hiphotos.baidu.com/image/h%3D360/sign=8dbd4f2003e9390149028b384bed54f9/a9d3fd1f4134970ac12ae72f97cad1c8a7865d51.jpg", "http://h.hiphotos.baidu.com/image/h%3D360/sign=da2b333738f33a87816d061cf65c1018/8d5494eef01f3a2922e765c99b25bc315c607c8d.jpg", "http://h.hiphotos.baidu.com/image/h%3D360/sign=9cd86f8b074f78f09f0b9cf549300a83/63d0f703918fa0ec6fcf95d2229759ee3c6ddbe1.jpg", "http://e.hiphotos.baidu.com/image/h%3D360/sign=71dbf9ca41166d222777139276230945/5882b2b7d0a20cf4158d80de74094b36acaf9995.jpg", "http://g.hiphotos.baidu.com/image/h%3D360/sign=58a05fe0b11c8701c9b6b4e0177e9e6e/0d338744ebf81a4c27dc0dcdd52a6059242da6cc.jpg", "http://c.hiphotos.baidu.com/image/h%3D360/sign=bd42bd17cdfcc3ceabc0cf35a245d6b7/cefc1e178a82b901b0c2f206768da9773912ef9c.jpg", "http://c.hiphotos.baidu.com/image/h%3D360/sign=ca781b34b8a1cd111ab674268913c8b0/b219ebc4b74543a9b08205c81c178a82b901141c.jpg", "http://h.hiphotos.baidu.com/image/h%3D360/sign=961d4668f203738dc14a0a24831ab073/08f790529822720e23efdb327fcb0a46f31fabd0.jpg", "http://e.hiphotos.baidu.com/image/h%3D360/sign=69f36e3438f33a87816d061cf65d1018/8d5494eef01f3a29913f38ca9b25bc315c607c3b.jpg", "http://b.hiphotos.baidu.com/image/h%3D360/sign=7be3a2dae2fe9925d40c6f5604a95ee4/d53f8794a4c27d1e93d181db19d5ad6eddc4383d.jpg", "http://f.hiphotos.baidu.com/image/h%3D360/sign=aca7546fb119ebc4df78709fb227cf79/279759ee3d6d55fb676d1d2c6f224f4a20a4dd15.jpg", "http://a.hiphotos.baidu.com/image/h%3D360/sign=d8ffa9ab013b5bb5a1d726f806d2d523/a6efce1b9d16fdfa5f9a6eeeb68f8c5494ee7b38.jpg", "http://b.hiphotos.baidu.com/image/h%3D360/sign=6329923a938fa0ec60c7620b1696594a/9213b07eca8065380d2ef41a95dda144ac3482f6.jpg", "http://e.hiphotos.baidu.com/image/h%3D360/sign=2837d4cbf9edab646b724bc6c736af81/8b13632762d0f7031db73fdc0afa513d2697c5ad.jpg", "http://a.hiphotos.baidu.com/image/h%3D360/sign=b99475f3d3a20cf45990f8d946094b0c/9d82d158ccbf6c81601cde9cbf3eb13533fa409f.jpg", "http://h.hiphotos.baidu.com/image/h%3D360/sign=b56e9e3d79f40ad10ae4c1e5672c1151/d439b6003af33a875645a91fc45c10385343b58f.jpg", "http://g.hiphotos.baidu.com/image/h%3D360/sign=de93277e97cad1c8cfbbfa214f3f67c4/83025aafa40f4bfb1530a905014f78f0f63618fa.jpg", "http://b.hiphotos.baidu.com/image/h%3D360/sign=3fbffe3dce134954611eee62664f92dd/ac6eddc451da81cb58bbc5715766d01609243128.jpg", "http://e.hiphotos.baidu.com/image/h%3D360/sign=e86cf529ceef7609230b9f991edca301/6d81800a19d8bc3e55adf77e878ba61ea9d345d9.jpg", "http://d.hiphotos.baidu.com/image/h%3D360/sign=71344afbb47eca800d053fe1a1229712/5fdf8db1cb134954ca0604bc524e9258d0094aca.jpg", "http://h.hiphotos.baidu.com/image/h%3D360/sign=78b729229f16fdfac76cc0e8848e8cea/cc11728b4710b91217a424f7c1fdfc0392452256.jpg", "http://b.hiphotos.baidu.com/image/h%3D360/sign=1b73d383bc12c8fcabf3f0cbcc0292b4/8326cffc1e178a82d0f9de03f303738da877e848.jpg", "http://b.hiphotos.baidu.com/image/h%3D360/sign=0aae89397df0f736c7fe4a073a54b382/f603918fa0ec08faf0f7ace15cee3d6d54fbda85.jpg", "http://h.hiphotos.baidu.com/image/h%3D360/sign=348d3f228501a18befeb1449ae2e0761/8644ebf81a4c510f66a05b146259252dd42aa55c.jpg", "http://d.hiphotos.baidu.com/image/h%3D360/sign=4a8fffc080025aafcc3278cdcbecab8d/f3d3572c11dfa9ec118fef1760d0f703918fc109.jpg", "http://c.hiphotos.baidu.com/image/h%3D360/sign=3bf4d46e9a22720e64cee4fc4bcb0a3a/4a36acaf2edda3cc4edac92c03e93901213f92fb.jpg", "http://a.hiphotos.baidu.com/image/h%3D360/sign=30a0c8e1c1fdfc03fa78e5bee43f87a9/8b82b9014a90f6038bd1a2583b12b31bb051ed48.jpg", "http://d.hiphotos.baidu.com/image/h%3D360/sign=df0f29a3eff81a4c3932eacfe72b6029/2e2eb9389b504fc2065e2bd2e1dde71191ef6de0.jpg", "http://d.hiphotos.baidu.com/image/h%3D360/sign=9f6e24765adf8db1a32e7a623922dddb/0ff41bd5ad6eddc492d491153ddbb6fd52663328.jpg", "http://a.hiphotos.baidu.com/image/h%3D360/sign=e1e4d8e0314e251ffdf7e2fe9784c9c2/d01373f082025aaf04df0411faedab64024f1a7e.jpg", "http://f.hiphotos.baidu.com/image/h%3D360/sign=541fcde0ce11728b2f2d8a24f8fcc3b3/eac4b74543a982264ae369568982b9014a90eba5.jpg", "http://c.hiphotos.baidu.com/image/h%3D360/sign=bbcbb65457fbb2fb2b2b5e147f4a2043/a044ad345982b2b7328787cd33adcbef76099b9b.jpg", "http://c.hiphotos.baidu.com/image/h%3D360/sign=742cd8a34fed2e73e3e9802ab701a16d/6a63f6246b600c33c512e9ef1f4c510fd9f9a1a9.jpg", "http://g.hiphotos.baidu.com/image/h%3D360/sign=66b0e6e1be3eb1355bc7b1bd961fa8cb/7a899e510fb30f24a41d260aca95d143ad4b0330.jpg", "http://c.hiphotos.baidu.com/image/h%3D360/sign=15e6852602082838770dda128898a964/d62a6059252dd42a1aad2f92063b5bb5c9eab866.jpg", "http://a.hiphotos.baidu.com/image/h%3D360/sign=8936bfe687025aafcc3278cdcbedab8d/f3d3572c11dfa9ecd236af3167d0f703918fc1f8.jpg", "http://d.hiphotos.baidu.com/image/h%3D360/sign=1eb47ece9b25bc31345d079e6edf8de7/8694a4c27d1ed21b3db585e5af6eddc451da3fb5.jpg", "http://h.hiphotos.baidu.com/image/h%3D360/sign=76b94c916209c93d18f208f1af3cf8bb/aa64034f78f0f736106d4a9e0f55b319eac41399.jpg", "http://a.hiphotos.baidu.com/image/h%3D360/sign=c3722ee78f1001e9513c1209880f7b06/a71ea8d3fd1f4134f7b46270271f95cad1c85e38.jpg", "http://d.hiphotos.baidu.com/image/h%3D360/sign=1eb47ece9b25bc31345d079e6edf8de7/8694a4c27d1ed21b3db585e5af6eddc451da3fb5.jpg", "http://c.hiphotos.baidu.com/image/h%3D360/sign=52d6ae6990cad1c8cfbbfa214f3f67c4/83025aafa40f4bfb99752012064f78f0f63618ca.jpg", "http://d.hiphotos.baidu.com/image/h%3D360/sign=7067c136f236afc3110c39638318eb85/908fa0ec08fa513d55bfe028386d55fbb2fbd92b.jpg", "http://f.hiphotos.baidu.com/image/h%3D360/sign=6035c56d3c01213fd03348da64e636f8/fc1f4134970a304ec83a514ed4c8a786c9175c6e.jpg", "http://a.hiphotos.baidu.com/image/h%3D360/sign=bf06263337fae6cd13b4ad673fb30f9e/29381f30e924b899812cbe3c6c061d950a7bf6a6.jpg", "http://a.hiphotos.baidu.com/image/h%3D360/sign=c15e44bacb177f3e0f34fa0b40ce3bb9/4afbfbedab64034f6866140aadc379310a551d16.jpg", "http://c.hiphotos.baidu.com/image/h%3D360/sign=2bf4c65457fbb2fb2b2b5e147f4a2043/a044ad345982b2b7a2b8f7cd33adcbef76099b90.jpg", "http://a.hiphotos.baidu.com/image/h%3D360/sign=c1b4f92ab251f819ee25054ceab54a76/d6ca7bcb0a46f21f46af2152f4246b600c33ae1c.jpg", "http://d.hiphotos.baidu.com/image/h%3D360/sign=7e192e2a4b540923b5696578a259d1dc/dcc451da81cb39db4555b75fd2160924ab183026.jpg", "http://g.hiphotos.baidu.com/image/h%3D360/sign=d7346f2a612762d09f3ea2b990ed0849/5243fbf2b2119313cea62fb967380cd790238dd6.jpg" }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); GridView gridView = (GridView) findViewById(R.id.gridView); gridView.setAdapter(new PhotoWallAdapter(MainActivity.this,imgs,gridView)); } }
布局文件activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.ts.mylrucache.MainActivity"> <GridView android:layout_width="match_parent" android:layout_height="match_parent" android:columnWidth="120dp" android:stretchMode="columnWidth" android:numColumns="auto_fit" android:verticalSpacing="10dp" android:gravity="center" android:id="@+id/gridView" /> </RelativeLayout>
gridview_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/imageView" android:layout_width="120dp" android:layout_height="120dp" android:src="@mipmap/ic_launcher" android:layout_centerInParent="true" /> </LinearLayout>
适配器代码 PhotoWallAdapter
import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.util.Log; import android.util.LruCache; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.GridView; import android.widget.ImageView; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; /** *照片墙的适配器,测试能缓存24张图片 */ public class PhotoWallAdapter extends BaseAdapter implements AbsListView.OnScrollListener{ private Context context; private GridView gridView; /** * 记录所有正在下载或等待下载的任务。 */ private ArrayList<BitmapWorkerTask> taskCollection; /** * 装所有图片的地址 */ private String[] imgsUrl; /** * 主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中, * 并且把最近最少使用的对象在缓存值达到预设定值之前从内存中移除。 */ private LruCache<String,Bitmap> memoryCache;//内存缓存,当做map来看就是 /** * 当前屏幕第一张可见图片的下标 */ private int mFirstVisibleItem; /** * 一屏有多少张图片可见 */ private int mVisibleItemCount; /** * 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。 */ private boolean isFirstEnter = true; public PhotoWallAdapter(Context context,String[] imgsUrl,GridView gridView) { this.context=context; this.imgsUrl = imgsUrl; this.gridView=gridView; taskCollection=new ArrayList<BitmapWorkerTask>(); int maxMemory = (int) (Runtime.getRuntime().maxMemory());//获取应用的最大可用内存 Log.i("TAG", "该应用的最大可用内存为:" + maxMemory/1024 + "KB");//64M,1Kb=1024个字节 //使用最大可用内存值的1/8作为缓存的大小 int cacheSize=maxMemory / 8; memoryCache=new LruCache<String,Bitmap>(cacheSize){ @Override protected int sizeOf(String key, Bitmap value) { //重写此方法来衡量每张图片的大小,默认返回的1 return value.getByteCount(); } }; gridView.setOnScrollListener(this);//设置滑动监听 } /** * 添加一个位图到内存中 * @param key 该位图在内存中的键 * @param bitmap 需要缓存的位图 */ public void addBitmapToMemoryCache(String key,Bitmap bitmap){ if(getBitmapFromMemCache(key) == null){ memoryCache.put(key, bitmap); Log.i("tag","缓存的个数:"+memoryCache.hitCount());//如果根据相应的key得到value,就增加一次命中hitCount Log.i("tag","没有命中(缓存)的个数:"+memoryCache.missCount());//否则增加一次missCount Log.i("tag","回收的个数:"+memoryCache.evictionCount()); Log.i("tag","调用create的次数:"+memoryCache.createCount());//createCount表示调用create的次数 Log.i("tag","总共添加的次数:"+memoryCache.putCount()); Log.i("tag","当前缓存的总大小:"+memoryCache.size()); } } /** *根据键获取内存中的bitmap */ private Bitmap getBitmapFromMemCache(String key) { return memoryCache.get(key); } @Override public int getCount() { return imgsUrl.length; } @Override public Object getItem(int position) { return imgsUrl[position]; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if(convertView==null){ holder=new ViewHolder(); convertView=View.inflate(context, R.layout.gridview_item, null); holder.img= (ImageView) convertView.findViewById(R.id.imageView); convertView.setTag(holder); }else{ holder= (ViewHolder) convertView.getTag(); } holder.img.setTag(imgsUrl[position]); //先从缓存中获取 Bitmap bitmap = getBitmapFromMemCache(imgsUrl[position]); if(bitmap!=null){ holder.img.setImageBitmap(bitmap); }else{ //否则先设置默认图片,等待停止滑动执行onScrollStateChanged方法时再从网络上获取 holder.img.setImageResource(R.mipmap.ic_launcher); } return convertView; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 仅当GridView静止,手指离开屏幕时才去下载图片,GridView滑动时取消所有正在下载的任务 if (scrollState == SCROLL_STATE_IDLE) { loadBitmaps(mFirstVisibleItem, mVisibleItemCount);//从网络上获取图片 } else { cancelAllTasks();//取消所有正在下载任务 } } /** * 首次开启应用时会执行此方法 * 下载的任务应该由onScrollStateChanged里调用,但首次进入程序时onScrollStateChanged并不会调用 */ @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mFirstVisibleItem=firstVisibleItem;//记录当前屏幕第一个的下标 mVisibleItemCount=visibleItemCount;//屏幕显示的个数 if (isFirstEnter && visibleItemCount > 0) { loadBitmaps(firstVisibleItem, visibleItemCount);//下载图片 isFirstEnter = false; } } /** * 取消所有正在下载或等待下载的任务。 */ public void cancelAllTasks() { if (taskCollection != null) { for (BitmapWorkerTask task : taskCollection) { task.cancel(false); } taskCollection.removeAll(taskCollection); } } /** * 加载Bitmap对象。此方法会在LruCache中检查所有屏幕中可见的ImageView的Bitmap对象, * 如果发现任何一个ImageView的Bitmap对象不在缓存中,就会开启异步线程去下载图片。 * * @param firstVisibleItem * 第一个可见的ImageView的下标 * @param visibleItemCount * 屏幕中总共可见的元素数 */ private void loadBitmaps(int firstVisibleItem, int visibleItemCount) { try { for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) { String imageUrl = imgsUrl[i]; Bitmap bitmap = getBitmapFromMemCache(imageUrl); if (bitmap == null) { BitmapWorkerTask task = new BitmapWorkerTask(); taskCollection.add(task); task.execute(imageUrl); } else { //由于在getView方法中就设置了已经存在的缓存图片,所以这里没必要再次获取 /*ImageView imageView = (ImageView) gridView.findViewWithTag(imageUrl); if (imageView != null && bitmap != null) { imageView.setImageBitmap(bitmap); }*/ } } } catch (Exception e) { e.printStackTrace(); } } class ViewHolder { ImageView img; } /** * 异步下载图片 */ class BitmapWorkerTask extends AsyncTask<String,Void,Bitmap>{ private String imgUrl; @Override protected Bitmap doInBackground(String... params) { imgUrl=params[0]; Bitmap bitmap = downloadBitmap(imgUrl); if (bitmap != null) { // 图片下载完成后缓存到LrcCache中 addBitmapToMemoryCache(imgUrl, bitmap); } return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { // 根据Tag找到相应的ImageView控件,将下载好的图片显示出来。 ImageView imageView = (ImageView) gridView.findViewWithTag(imgUrl); if (imageView != null && bitmap != null) { imageView.setImageBitmap(bitmap); } taskCollection.remove(this);//图片下载完毕,从异步任务集合中删除 Log.i("tag","任务集合包含的个数:"+taskCollection.size()); } /** *根据网址下载图片 */ private Bitmap downloadBitmap(String imageUrl){ Bitmap bitmap = null; HttpURLConnection con = null; try { URL url = new URL(imageUrl); con = (HttpURLConnection) url.openConnection(); con.setConnectTimeout(5 * 1000); con.setReadTimeout(10 * 1000); //这里可以结合第一种方法,将图片压缩到一定的尺寸再存到内存中,使得LruCache能存更多的图片 bitmap = BitmapFactory.decodeStream(con.getInputStream()); } catch (Exception e) { e.printStackTrace(); } finally { if (con != null) { con.disconnect();//断开连接 } } return bitmap; } } }
效果图: