一、首先还是运行效果图
二、工程结构
三、进入代码阶段,首先三级缓存分为,内存缓存为一级,本地缓存即SD卡缓存为二级,最后是网络下载为三级
1、内存缓存
package com.example.imageloaddemotest.threelevelcache.utils; import android.graphics.Bitmap; import android.support.v4.util.LruCache; import android.util.Log; /** * 内存缓存 * Created by 袁磊 on 2017/4/14. */ public class MemoryCacheUtils { //图片缓存技术的核心类,用于缓存所有下载好的图片,在程序内存达到设定值时会将最少最近使用的图片移除掉。 private LruCache<String, Bitmap> mLruCache; public MemoryCacheUtils() { setCacheSize(); } /** * 设置缓存大小 */ private void setCacheSize() { // 获取应用程序最大可用内存 int maxMemory = (int) Runtime.getRuntime().maxMemory(); int cacheSize = maxMemory / 8; // 设置图片缓存大小为程序最大可用内存的1/8 mLruCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getByteCount(); } }; } /** * 将一张图片写入到内存 * * @param key LruCache的键,这里传入图片的URL地址。 * @param bitmap LruCache的键,这里传入从网络上下载的Bitmap对象。 */ public void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemoryCache(key) == null) { mLruCache.put(key, bitmap); } } /** * 从内存中读取一张图片,如果不存在就返回null。 * * @param key LruCache的键,这里传入图片的URL地址。 * @return 对应传入键的Bitmap对象,或者null。 */ public Bitmap getBitmapFromMemoryCache(String key) { Log.d("MyBitmapUtils", "从内存中加载图片"); return mLruCache.get(key); } }
MemoryCacheUtils
2、本地缓存
package com.example.imageloaddemotest.threelevelcache.utils; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.os.Environment; import android.util.Log; import android.widget.ImageView; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.security.NoSuchAlgorithmException; import java.util.HashSet; import java.util.Set; /** * 本地缓存 * Created by 袁磊 on 2017/4/14. */ public class SDcardCacheUtils { /** * 我们读取内存的绝对路径 */ public static final String CACHE_PATH = Environment.getExternalStorageDirectory() .getAbsolutePath() + "/ImgCacheDemo"; /** * 从本地读取 * * @param url */ public Bitmap getBitmapFromSDcardCache(String url) { String fileName = null; try { //得到图片的url的md5的文件名 fileName = MD5Encoder.getMD5(url); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } File file = new File(CACHE_PATH, fileName); //如果存在,就通过bitmap工厂,返回的bitmap,然后返回bitmap if (file.exists()) { try { Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file)); Log.d("MyBitmapUtils", "从本地读取图片"); return bitmap; } catch (FileNotFoundException e) { e.printStackTrace(); } } return null; } /** * 向本地缓存 * * @param url 图片地址 * @param bitmap 图片 */ public void addBitmapToSDcardCache(String url, Bitmap bitmap) { String fileName = null; try { //我们对图片的地址进行MD5加密,作为文件名 fileName = MD5Encoder.getMD5(url); } catch (Exception e) { e.printStackTrace(); } /** * 以CACHE_PATH为文件夹 fileName为文件名 */ File file = new File(CACHE_PATH, fileName); //我们首先得到他的父文件 File parentFile = file.getParentFile(); //查看是否存在,如果不存在就创建 if (!parentFile.exists()) { parentFile.mkdirs(); //创建文件夹 } try { //将图片保存到本地 /** * @param format The format of the compressed image 图片的保存格式 * @param quality Hint to the compressor, 0-100. 0 meaning compress for * small size, 100 meaning compress for max quality. Some * formats, like PNG which is lossless, will ignore the * quality setting * 图片的保存的质量 100最好 * @param stream The outputstream to write the compressed data. */ bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file)); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
SDcardCacheUtils
本地缓存图片Md5加密工具类
package com.example.imageloaddemotest.threelevelcache.utils; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * Created by 袁磊 on 2017/4/14. */ public class MD5Encoder { public static String getMD5(String val) throws NoSuchAlgorithmException { MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update(val.getBytes()); byte[] m = md5.digest();//加密 return getString(m); } private static String getString(byte[] b) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < b.length; i++) { sb.append(b[i]); } return sb.toString(); } }
MD5Encoder
3、网络下载
package com.example.imageloaddemotest.threelevelcache.utils; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.util.Log; import android.widget.ImageView; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashSet; import java.util.Set; /** * 网络缓存 * Created by 袁磊 on 2017/4/14. */ public class NetCacheUtils { /** * 建立HTTP请求,并获取Bitmap对象。 * * @param imageUrl 图片的URL地址 * @return 解析后的Bitmap对象 */ public Bitmap downLoadBitmap(String imageUrl) { Bitmap bitmap = null; HttpURLConnection conn = null; try { URL url = new URL(imageUrl); conn = (HttpURLConnection) url.openConnection(); conn.setReadTimeout(5000);//设置读取超时 conn.setRequestMethod("GET"); //设置请求方法 conn.setConnectTimeout(5000); //设置连接超时连接 conn.connect(); //连接 int code = conn.getResponseCode();//响应码 if (code == 200) { //请求正确的响应码是200 InputStream inputStream = conn.getInputStream();//得到响应流 bitmap = BitmapFactory.decodeStream(inputStream);//得到bitmap对象 return bitmap; } } catch (IOException e) { e.printStackTrace(); } finally { assert conn != null; conn.disconnect(); } return null; } }
NetCacheUtils
4、管理内存,本地,网络,三级缓存的工具类
package com.example.imageloaddemotest.threelevelcache.utils; import android.graphics.Bitmap; import android.os.AsyncTask; import android.util.Log; import android.widget.AbsListView; import android.widget.GridView; import android.widget.ImageView; import com.example.imageloaddemotest.R; import com.example.imageloaddemotest.constant.ImageUrls; import com.example.imageloaddemotest.threelevelcache.utils.MemoryCacheUtils; import com.example.imageloaddemotest.threelevelcache.utils.NetCacheUtils; import com.example.imageloaddemotest.threelevelcache.utils.SDcardCacheUtils; import java.util.HashSet; import java.util.Set; /** * 可以传入GridView监听滑动事件来优化此类 * 自定义的bitmap工具类 * Created by 袁磊 on 2017/4/15. */ public class MyBitmapUtils implements AbsListView.OnScrollListener { /** * GridView实例 */ private GridView mGridView; /** * 图片地址 */ private String[] imageUrls; /** * 第一张可见图片的下标 */ private int mFirstVisibleItem; /** * 一屏有多少张图片可见 */ private int mVisibleItemCount; /** * 记录是否刚打开程序,用于解决进入程序不滚动屏幕,不会下载图片的问题。 */ private boolean isFirstEnter = true; /** * 记录所有正在获取图片或等待获取图片的任务。 */ private Set<MyAsyncTask> taskCollection; /** * 网络缓存 */ public NetCacheUtils mNetCacheUtils; /** * 本地缓存 */ public SDcardCacheUtils mSdCacheUtils; /** * 内存缓存 */ public MemoryCacheUtils mMemoryCacheUtils; public MyBitmapUtils(GridView mGridView, String[] imageUrls) { this.mGridView = mGridView; this.imageUrls = imageUrls; mGridView.setOnScrollListener(this); taskCollection = new HashSet<>(); mMemoryCacheUtils = new MemoryCacheUtils(); mSdCacheUtils = new SDcardCacheUtils(); mNetCacheUtils = new NetCacheUtils(); } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // 仅当GridView静止时才去下载图片,GridView滑动时取消所有正在下载的任务 if (scrollState == SCROLL_STATE_IDLE) { loadBitmaps(mFirstVisibleItem, mVisibleItemCount); } else { cancelAllTasks(); } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { mFirstVisibleItem = firstVisibleItem; mVisibleItemCount = visibleItemCount; // 下载的任务应该由onScrollStateChanged里调用, // 但首次进入程序时onScrollStateChanged并不会调用, // 因此在这里为首次进入程序开启下载任务。 if (isFirstEnter && visibleItemCount > 0) { loadBitmaps(firstVisibleItem, visibleItemCount); isFirstEnter = false; } } /** * 取消所有正在下载或等待下载的任务。 */ public void cancelAllTasks() { if (taskCollection != null) { for (MyAsyncTask task : taskCollection) { task.cancel(false); } } } /** * 加载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 = imageUrls[i]; //根据Tag获取相应位置的ImageView ImageView imageView = (ImageView) mGridView.findViewWithTag(imageUrl); new MyAsyncTask().execute(imageView, imageUrl); } } catch (Exception e) { e.printStackTrace(); } } /** * 第一个泛型 : 参数类型 对应doInBackground() * 第二个泛型 : 更新进度 对应onProgressUpdate() * 第三个泛型 : 返回结果result 对应onPostExecute */ class MyAsyncTask extends AsyncTask<Object, Void, Bitmap> { private ImageView mImageView; private String mImageUrl; @Override protected Bitmap doInBackground(Object... params) { mImageView = (ImageView) params[0]; mImageUrl = (String) params[1]; //根据Tag获取相应位置的ImageView mImageView = (ImageView) mGridView.findViewWithTag(mImageUrl); //检查内存中是否有缓存 Bitmap bitmap = mMemoryCacheUtils.getBitmapFromMemoryCache(mImageUrl); if (bitmap == null) { //检查本地缓存是否存在 bitmap = mSdCacheUtils.getBitmapFromSDcardCache(mImageUrl); if (bitmap == null) { //从网络下载 bitmap = mNetCacheUtils.downLoadBitmap(mImageUrl); if (bitmap != null) { //当从网络上下载好之后保存到sdcard中 mSdCacheUtils.addBitmapToSDcardCache(mImageUrl, bitmap); //将图片写入到内存 mMemoryCacheUtils.addBitmapToMemoryCache(mImageUrl, bitmap); return bitmap; } } else { if (mImageView != null) { //将图片写入到内存 mMemoryCacheUtils.addBitmapToMemoryCache(mImageUrl, bitmap); return bitmap; } } } else { if (mImageView != null) { return bitmap; } } return null; } @Override protected void onPostExecute(Bitmap bitmap) { //回调结果,耗时方法结束后,主线程 super.onPostExecute(bitmap); if (bitmap != null && mImageView != null) { //得到图片的tag值 String url = (String) mImageView.getTag(); //确保图片设置给了正确的image if (url.equals(mImageUrl)) { mImageView.setImageBitmap(bitmap); Log.d("MyBitmapUtils", "我是从网络缓存中读取的图片啊"); } } } } /** * 给ImageView设置图片。首先从LruCache中取出图片的缓存,设置到ImageView上。 * 如果LruCache中没有该图片的缓存,就给ImageView设置一张默认图片。 * 因为本地缓存取出,存入比较耗时,故此方法中不检查本地缓存 * * @param imageUrl 图片的URL地址,用于作为LruCache的键。 * @param imageView 用于显示图片的控件。 */ public void setImageView(String imageUrl, ImageView imageView) { //检查内存中是否存在缓存 Bitmap bitmap = mMemoryCacheUtils.getBitmapFromMemoryCache(imageUrl); if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { imageView.setImageResource(R.drawable.empty_photo); } } }
MyBitmapUtils
四、三级缓存的使用
1、在适配器中
package com.example.imageloaddemotest.threelevelcache.adapter; import android.content.Context; import android.graphics.Bitmap; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.GridView; import android.widget.ImageView; import com.example.imageloaddemotest.R; import com.example.imageloaddemotest.threelevelcache.utils.MyBitmapUtils; /** * Created by 袁磊 on 2017/4/15. */ public class ThreeLevelAdatper extends BaseAdapter { private String[] urls; private LayoutInflater inflater; /** * 缓存工具类 */ private MyBitmapUtils utils; public ThreeLevelAdatper(Context context, String[] urls, GridView gridView) { this.inflater = LayoutInflater.from(context); this.urls = urls; //传入GridView和资源地址 utils = new MyBitmapUtils(gridView, urls); } @Override public int getCount() { return urls == null ? 0 : urls.length; } @Override public Object getItem(int position) { return urls[position]; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { Holder holder = null; if (convertView == null) { holder = new Holder(); convertView = inflater.inflate(R.layout.item_only_image, null); holder.imageView = (ImageView) convertView.findViewById(R.id.iv_item_onlyImg); convertView.setTag(holder); } else { holder = (Holder) convertView.getTag(); } String url = (String) getItem(position); //设置Tag holder.imageView.setTag(url); //检查内存缓存,没有则设置默认图片 utils.setImageView(url, holder.imageView); return convertView; } static class Holder { ImageView imageView; } }
ThreeLevelAdatper
适配器布局文件
<?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" android:orientation="vertical"> <ImageView android:id="@+id/iv_item_onlyImg" android:layout_width="180dp" android:layout_height="180dp" android:scaleType="center" /> </LinearLayout>
item_only_image
2、Activity代码中
package com.example.imageloaddemotest.threelevelcache.activity; import android.app.Activity; import android.os.Bundle; import android.widget.GridView; import com.example.imageloaddemotest.R; import com.example.imageloaddemotest.constant.ImageUrls; import com.example.imageloaddemotest.threelevelcache.adapter.ThreeLevelAdatper; /** * Created by 袁磊 on 2017/4/15. */ public class ThreeLeveCacheActivity extends Activity { private GridView gvThreeLevel; private ThreeLevelAdatper adatper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_threelevelcache); gvThreeLevel = (GridView) findViewById(R.id.gv_threeLevelCache); adatper = new ThreeLevelAdatper(this, ImageUrls.imageUrls, gvThreeLevel); gvThreeLevel.setAdapter(adatper); } }
ThreeLeveCacheActivity
Activity布局文件
<?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" android:orientation="vertical" android:padding="5dp"> <GridView android:id="@+id/gv_threeLevelCache" android:layout_width="match_parent" android:layout_height="match_parent" android:horizontalSpacing="5dp" android:numColumns="auto_fit" android:verticalSpacing="5dp" /> </LinearLayout>
activity_threelevelcache
3、最后是资源地址类,两个数组可供选择,一个是百度图片地址,一个是郭霖大神上传的csdn图片地址
package com.example.imageloaddemotest.constant; /** * Created by 袁磊 on 2017/4/13. */ public class ImageUrls { public final static String[] imageUrls = new String[]{ "http://img.my.csdn.net/uploads/201309/01/1378037235_3453.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037235_9280.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037234_3539.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037234_6318.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037194_2965.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037193_1687.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037193_1286.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037192_8379.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037178_9374.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037177_1254.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037177_6203.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037152_6352.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037151_9565.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037151_7904.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037148_7104.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037129_8825.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037128_5291.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037128_3531.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037127_1085.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037095_7515.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037094_8001.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037093_7168.jpg", "http://img.my.csdn.net/uploads/201309/01/1378037091_4950.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949643_6410.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949642_6939.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949630_4505.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949630_4593.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949629_7309.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949629_8247.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949615_1986.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949614_8482.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949614_3743.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949614_4199.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949599_3416.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949599_5269.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949598_7858.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949598_9982.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949578_2770.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949578_8744.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949577_5210.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949577_1998.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949482_8813.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949481_6577.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949480_4490.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949455_6792.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949455_6345.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949442_4553.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949441_8987.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949441_5454.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949454_6367.jpg", "http://img.my.csdn.net/uploads/201308/31/1377949442_4562.jpg"}; public static final String[] imageUrls2 = { "http://scimg.jb51.net/allimg/160316/14-160316145129313.jpg", "http://img1.3lian.com/2015/w7/85/d/21.jpg", "http://pic13.nipic.com/20110413/2019141_200652522350_2.jpg", "http://pic4.nipic.com/20091121/3764872_215617048242_2.jpg", "http://pic.58pic.com/58pic/11/79/85/13t58PICsap.jpg", "http://img4.imgtn.bdimg.com/it/u=3776739438,757564394&fm=214&gp=0.jpg", "http://pic.58pic.com/58pic/13/86/90/43A58PICuUn_1024.jpg", "http://img3.3lian.com/2014/c2/96/d/44.jpg", "http://pic38.nipic.com/20140228/3822951_135521683000_2.jpg", "http://img1.3lian.com/2015/a1/113/d/10.jpg", "http://pic27.nipic.com/20130319/11935511_225831392000_2.jpg", "http://pic.58pic.com/58pic/15/15/32/43x58PICgE2_1024.jpg", "http://imgsrc.baidu.com/forum/pic/item/343ad4b52fc0d8a0b0fb9575.jpg", "http://img.taopic.com/uploads/allimg/121126/240453-1211261I64263.jpg", "http://pic.58pic.com/58pic/13/75/82/36c58PICPZI_1024.jpg", "http://img17.3lian.com/d/file/201701/23/22caec632f605230b882959614369660.jpg", "http://img.taopic.com/uploads/allimg/121203/267873-121203225I999.jpg", "http://sc.jb51.net/uploads/allimg/140428/10-14042P10916240.jpg", "http://pic1a.nipic.com/2008-12-19/2008121995135850_2.jpg" }; }
ImageUrls
4、MainActivity中就一个跳转按钮,就不贴代码了
五、此篇三级缓存感谢两位大神博客:郭霖大神:http://blog.csdn.net/guolin_blog/article/details/9526203
若兰明月:http://blog.csdn.net/wuyinlei/article/details/50606455
源码已上传至GitHub:https://github.com/gh1225987336/ThreeLevelCache
时间: 2024-10-11 18:57:39