Android照片墙完整版,完美结合LruCache和DiskLruCache
2016-06-23 19:46
766 查看
实现效果:
1,DiskLruCache(非Google官方编写,但获得官方认证)。Android Doc中并没有对DiskLruCache的用法给出详细的说明,需要下载DiskLruCache.Java文件,拷贝到自己的项目中。
2,图片加载器,封装了线程池,LruCache,DiskLruCache,实现图片的内存,本地二级缓存
ImageLoader.java:
MainActivity.java:
源码下载地址,点击这里
参考资料:
http://blog.csdn.net/guolin_blog/article/details/28863651
http://blog.csdn.net/fangzhibin4712/article/details/38823533
1,DiskLruCache(非Google官方编写,但获得官方认证)。Android Doc中并没有对DiskLruCache的用法给出详细的说明,需要下载DiskLruCache.Java文件,拷贝到自己的项目中。
2,图片加载器,封装了线程池,LruCache,DiskLruCache,实现图片的内存,本地二级缓存
ImageLoader.java:
package com.hdc.testdiskcache.loader; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.Environment; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.StatFs; import android.support.v4.util.LruCache; import android.util.Log; import android.widget.ImageView; import com.hdc.testdiskcache.R; import com.hdc.testdiskcache.utils.MyUtils; public class ImageLoader { private static final String TAG = "ImageLoader"; public static final int MESSAGE_POST_RESULT = 1; private static final int CPU_COUNT = Runtime.getRuntime() .availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final long KEEP_ALIVE = 10L; private static final int TAG_KEY_URI = R.id.imageloader_uri; private static final long DISK_CACHE_SIZE = 1024 * 1024 * 50; private static final int IO_BUFFER_SIZE = 8 * 1024; private static final int DISK_CACHE_INDEX = 0; private boolean mIsDiskLruCacheCreated = false; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "ImageLoader#" + mCount.getAndIncrement()); } }; public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), sThreadFactory); private Handler mMainHandler = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(Message msg) { LoaderResult result = (LoaderResult) msg.obj; ImageView imageView = result.imageView; imageView.setImageBitmap(result.bitmap); String uri = (String) imageView.getTag(TAG_KEY_URI); if (uri.equals(result.uri)) { imageView.setImageBitmap(result.bitmap); } else { Log.w(TAG, "set image bitmap,but url has changed, ignored!"); } }; }; private Context mContext; private ImageResizer mImageResizer = new ImageResizer(); private LruCache<String, Bitmap> mMemoryCache; private DiskLruCache mDiskLruCache; private ImageLoader(Context context) { mContext = context.getApplicationContext(); int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { return bitmap.getRowBytes() * bitmap.getHeight() / 1024; } }; File diskCacheDir = getDiskCacheDir(mContext, "bitmap"); if (!diskCacheDir.exists()) { diskCacheDir.mkdirs(); } if (getUsableSpace(diskCacheDir) > DISK_CACHE_SIZE) { try { mDiskLruCache = DiskLruCache.open(diskCacheDir, 1, 1, DISK_CACHE_SIZE); mIsDiskLruCacheCreated = true; } catch (IOException e) { e.printStackTrace(); } } } /** * build a new instance of ImageLoader * @param context * @return a new instance of ImageLoader */ public static ImageLoader build(Context context) { return new ImageLoader(context); } private void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (getBitmapFromMemCache(key) == null) { mMemoryCache.put(key, bitmap); } } private Bitmap getBitmapFromMemCache(String key) { return mMemoryCache.get(key); } /** * load bitmap from memory cache or disk cache or network async, then bind imageView and bitmap. * NOTE THAT: should run in UI Thread * @param uri http url * @param imageView bitmap's bind object */ public void bindBitmap(final String uri, final ImageView imageView) { bindBitmap(uri, imageView, 0, 0); } public void bindBitmap(final String uri, final ImageView imageView, final int reqWidth, final int reqHeight) { imageView.setTag(TAG_KEY_URI, uri); Bitmap bitmap = loadBitmapFromMemCache(uri); if (bitmap != null) { imageView.setImageBitmap(bitmap); return; } Runnable loadBitmapTask = new Runnable() { @Override public void run() { Bitmap bitmap = loadBitmap(uri, reqWidth, reqHeight); if (bitmap != null) { LoaderResult result = new LoaderResult(imageView, uri, bitmap); mMainHandler.obtainMessage(MESSAGE_POST_RESULT, result).sendToTarget(); } } }; THREAD_POOL_EXECUTOR.execute(loadBitmapTask); } /** * load bitmap from memory cache or disk cache or network. * @param uri http url * @param reqWidth the width ImageView desired * @param reqHeight the height ImageView desired * @return bitmap, maybe null. */ public Bitmap loadBitmap(String uri, int reqWidth, int reqHeight) { Bitmap bitmap = loadBitmapFromMemCache(uri); if (bitmap != null) { Log.d(TAG, "loadBitmapFromMemCache,url:" + uri); return bitmap; } try { bitmap = loadBitmapFromDiskCache(uri, reqWidth, reqHeight); if (bitmap != null) { Log.d(TAG, "loadBitmapFromDisk,url:" + uri); return bitmap; } bitmap = loadBitmapFromHttp(uri, reqWidth, reqHeight); Log.d(TAG, "loadBitmapFromHttp,url:" + uri); } catch (IOException e) { e.printStackTrace(); } if (bitmap == null && !mIsDiskLruCacheCreated) { Log.w(TAG, "encounter error, DiskLruCache is not created."); bitmap = downloadBitmapFromUrl(uri); } return bitmap; } private Bitmap loadBitmapFromMemCache(String url) { final String key = hashKeyFormUrl(url); Bitmap bitmap = getBitmapFromMemCache(key); return bitmap; } private Bitmap loadBitmapFromHttp(String url, int reqWidth, int reqHeight) throws IOException { if (Looper.myLooper() == Looper.getMainLooper()) { throw new RuntimeException("can not visit network from UI Thread."); } if (mDiskLruCache == null) { return null; } String key = hashKeyFormUrl(url); DiskLruCache.Editor editor = mDiskLruCache.edit(key); if (editor != null) { OutputStream outputStream = editor.newOutputStream(DISK_CACHE_INDEX); if (downloadUrlToStream(url, outputStream)) { editor.commit(); } else { editor.abort(); } mDiskLruCache.flush(); } return loadBitmapFromDiskCache(url, reqWidth, reqHeight); } private Bitmap loadBitmapFromDiskCache(String url, int reqWidth, int reqHeight) throws IOException { if (Looper.myLooper() == Looper.getMainLooper()) { Log.w(TAG, "load bitmap from UI Thread, it's not recommended!"); } if (mDiskLruCache == null) { return null; } Bitmap bitmap = null; String key = hashKeyFormUrl(url); DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); if (snapShot != null) { FileInputStream fileInputStream = (FileInputStream)snapShot.getInputStream(DISK_CACHE_INDEX); FileDescriptor fileDescriptor = fileInputStream.getFD(); bitmap = mImageResizer.decodeSampledBitmapFromFileDescriptor(fileDescriptor, reqWidth, reqHeight); if (bitmap != null) { addBitmapToMemoryCache(key, bitmap); } } return bitmap; } public boolean downloadUrlToStream(String urlString, OutputStream outputStream) { HttpURLConnection urlConnection = null; BufferedOutputStream out = null; BufferedInputStream in = null; try { final URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE); out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE); int b; while ((b = in.read()) != -1) { out.write(b); } return true; } catch (IOException e) { Log.e(TAG, "downloadBitmap failed." + e); } finally { if (urlConnection != null) { urlConnection.disconnect(); } MyUtils.close(out); MyUtils.close(in); } return false; } private Bitmap downloadBitmapFromUrl(String urlString) { Bitmap bitmap = null; HttpURLConnection urlConnection = null; BufferedInputStream in = null; try { final URL url = new URL(urlString); urlConnection = (HttpURLConnection) url.openConnection(); in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE); bitmap = BitmapFactory.decodeStream(in); } catch (final IOException e) { Log.e(TAG, "Error in downloadBitmap: " + e); } finally { if (urlConnection != null) { urlConnection.disconnect(); } MyUtils.close(in); } return bitmap; } private String hashKeyFormUrl(String url) { String cacheKey; try { final MessageDigest mDigest = MessageDigest.getInstance("MD5"); mDigest.update(url.getBytes()); cacheKey = bytesToHexString(mDigest.digest()); } catch (NoSuchAlgorithmException e) { cacheKey = String.valueOf(url.hashCode()); } return cacheKey; } private String bytesToHexString(byte[] bytes) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < bytes.length; i++) { String hex = Integer.toHexString(0xFF & bytes[i]); if (hex.length() == 1) { sb.append('0'); } sb.append(hex); } return sb.toString(); } public File getDiskCacheDir(Context context, String uniqueName) { boolean externalStorageAvailable = Environment .getExternalStorageState().equals(Environment.MEDIA_MOUNTED); final String cachePath; if (externalStorageAvailable) { cachePath = context.getExternalCacheDir().getPath(); } else { cachePath = context.getCacheDir().getPath(); } return new File(cachePath + File.separator + uniqueName); } @TargetApi(VERSION_CODES.GINGERBREAD) private long getUsableSpace(File path) { if (Build.VERSION.SDK_INT >= VERSION_CODES.GINGERBREAD) { return path.getUsableSpace(); } final StatFs stats = new StatFs(path.getPath()); return (long) stats.getBlockSize() * (long) stats.getAvailableBlocks(); } private static class LoaderResult { public ImageView imageView; public String uri; public Bitmap bitmap; public LoaderResult(ImageView imageView, String uri, Bitmap bitmap) { this.imageView = imageView; this.uri = uri; this.bitmap = bitmap; } } }图片压缩类,ImageResizer.java:
package com.hdc.testdiskcache.loader; import java.io.FileDescriptor; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; public class ImageResizer { private static final String TAG = "ImageResizer"; public ImageResizer() { } public Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res, resId, options); } public Bitmap decodeSampledBitmapFromFileDescriptor(FileDescriptor fd, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFileDescriptor(fd, null, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFileDescriptor(fd, null, options); } public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { if (reqWidth == 0 || reqHeight == 0) { return 1; } // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; Log.d(TAG, "origin, w= " + width + " h=" + height); int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and // keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) { inSampleSize *= 2; } } Log.d(TAG, "sampleSize:" + inSampleSize); return inSampleSize; } }界面调用,实现照片墙,内存,本地,网络三级缓存,实现滑动不加载图片,解决图片错位问题.
MainActivity.java:
package com.hdc.testdiskcache; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.LayoutInflater; 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 com.hdc.testdiskcache.loader.ImageLoader; import com.hdc.testdiskcache.utils.MyUtils; import java.util.ArrayList; import java.util.List; public class MainActivity extends Activity implements AbsListView.OnScrollListener { private static final String TAG = "MainActivity"; private List<String> mUrList = new ArrayList<String>(); ImageLoader mImageLoader; private GridView mImageGridView; private BaseAdapter mImageAdapter; private boolean mIsGridViewIdle = true; private int mImageWidth = 0; private boolean mIsWifi = false; private boolean mCanGetBitmapFromNetWork = false; private ImageView mIv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); initView(); mImageLoader = ImageLoader.build(MainActivity.this); } private void initData() { String[] imageUrls = { "http://b.hiphotos.baidu.com/zhidao/pic/item/a6efce1b9d16fdfafee0cfb5b68f8c5495ee7bd8.jpg", "http://pic47.nipic.com/20140830/7487939_180041822000_2.jpg", "http://pic41.nipic.com/20140518/4135003_102912523000_2.jpg", "http://img2.imgtn.bdimg.com/it/u=1133260524,1171054226&fm=21&gp=0.jpg", "http://h.hiphotos.baidu.com/image/pic/item/3b87e950352ac65c0f1f6e9efff2b21192138ac0.jpg", "http://pic42.nipic.com/20140618/9448607_210533564001_2.jpg", "http://pic10.nipic.com/20101027/3578782_201643041706_2.jpg", "http://picview01.baomihua.com/photos/20120805/m_14_634797817549375000_37810757.jpg", "http://img2.3lian.com/2014/c7/51/d/26.jpg", "http://img3.3lian.com/2013/c1/34/d/93.jpg", "http://b.zol-img.com.cn/desk/bizhi/image/3/960x600/1375841395686.jpg", "http://picview01.baomihua.com/photos/20120917/m_14_634834710114218750_41852580.jpg", "http://cdn.duitang.com/uploads/item/201311/03/20131103171224_rr2aL.jpeg", "http://imgrt.pconline.com.cn/images/upload/upc/tx/wallpaper/1210/17/c1/spcgroup/14468225_1350443478079_1680x1050.jpg", "http://pic41.nipic.com/20140518/4135003_102025858000_2.jpg", "http://www.1tong.com/uploads/wallpaper/landscapes/200-4-730x456.jpg", "http://pic.58pic.com/58pic/13/00/22/32M58PICV6U.jpg", "http://picview01.baomihua.com/photos/20120629/m_14_634765948339062500_11778706.jpg", "http://h.hiphotos.baidu.com/zhidao/wh%3D450%2C600/sign=429e7b1b92ef76c6d087f32fa826d1cc/7acb0a46f21fbe09cc206a2e69600c338744ad8a.jpg", "http://pica.nipic.com/2007-12-21/2007122115114908_2.jpg", "http://cdn.duitang.com/uploads/item/201405/13/20140513212305_XcKLG.jpeg", "http://photo.loveyd.com/uploads/allimg/080618/1110324.jpg", "http://img4.duitang.com/uploads/item/201404/17/20140417105820_GuEHe.thumb.700_0.jpeg", "http://cdn.duitang.com/uploads/item/201204/21/20120421155228_i52eX.thumb.600_0.jpeg", "http://img4.duitang.com/uploads/item/201404/17/20140417105856_LTayu.thumb.700_0.jpeg", "http://img04.tooopen.com/images/20130723/tooopen_20530699.jpg", "http://www.qjis.com/uploads/allimg/120612/1131352Y2-16.jpg", "http://pic.dbw.cn/0/01/33/59/1335968_847719.jpg", "http://a.hiphotos.baidu.com/image/pic/item/a8773912b31bb051a862339c337adab44bede0c4.jpg", "http://h.hiphotos.baidu.com/image/pic/item/f11f3a292df5e0feeea8a30f5e6034a85edf720f.jpg", "http://img0.pconline.com.cn/pconline/bizi/desktop/1412/ER2.jpg", "http://pic.58pic.com/58pic/11/25/04/91v58PIC6Xy.jpg", "http://img3.3lian.com/2013/c2/32/d/101.jpg", "http://pic25.nipic.com/20121210/7447430_172514301000_2.jpg", "http://img02.tooopen.com/images/20140320/sy_57121781945.jpg", "http://www.renyugang.cn/emlog/content/plugins/kl_album/upload/201004/852706aad6df6cd839f1211c358f2812201004120651068641.jpg" }; for (String url : imageUrls) { mUrList.add(url); } int screenWidth = MyUtils.getScreenMetrics(this).widthPixels; int space = (int) MyUtils.dp2px(this, 20f); mImageWidth = (screenWidth - space) / 3; mIsWifi = MyUtils.isWifi(this); if (mIsWifi) { mCanGetBitmapFromNetWork = true; } } private void initView() { mImageGridView = (GridView) findViewById(R.id.gridView1); mIv = (ImageView) findViewById(R.id.iv); mImageAdapter = new ImageAdapter(this); mImageGridView.setAdapter(mImageAdapter); mImageGridView.setOnScrollListener(this); if (!mIsWifi) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("初次使用会从网络下载大概5MB的图片,确认要下载吗?"); builder.setTitle("注意"); builder.setPositiveButton("是", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { mCanGetBitmapFromNetWork = true; mImageAdapter.notifyDataSetChanged(); } }); builder.setNegativeButton("否", null); builder.show(); } mIv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mImageGridView.setVisibility(View.VISIBLE); } }); } private class ImageAdapter extends BaseAdapter { private LayoutInflater mInflater; private Drawable mDefaultBitmapDrawable; private ImageAdapter(Context context) { mInflater = LayoutInflater.from(context); mDefaultBitmapDrawable = context.getResources().getDrawable(R.drawable.image_default); } @Override public int getCount() { return mUrList.size(); } @Override public String getItem(int position) { return mUrList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = mInflater.inflate(R.layout.image_list_item, parent, false); holder = new ViewHolder(); holder.imageView = (ImageView) convertView.findViewById(R.id.image); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } ImageView imageView = holder.imageView; final String tag = (String) imageView.getTag(); final String uri = getItem(position); if (!uri.equals(tag)) { imageView.setImageDrawable(mDefaultBitmapDrawable); } if (mIsGridViewIdle && mCanGetBitmapFromNetWork) { imageView.setTag(uri); mImageLoader.bindBitmap(uri, imageView, mImageWidth, mImageWidth); } return convertView; } } private static class ViewHolder { public ImageView imageView; } @Override public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) { mIsGridViewIdle = true; mImageAdapter.notifyDataSetChanged(); } else { mIsGridViewIdle = false; } } @Override public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { // ignored } }
源码下载地址,点击这里
参考资料:
http://blog.csdn.net/guolin_blog/article/details/28863651
http://blog.csdn.net/fangzhibin4712/article/details/38823533
相关文章推荐
- Android开发中ImageLoder进行图片加载和缓存
- js实现超酷的照片墙展示效果图附源码下载
- IE图片缓存document.execCommand("BackgroundImageCache",false,true)
- IE6下CSS图片缓存问题解决方法
- javascript实现了照片拖拽点击置顶的照片墙代码
- Android图片加载缓存框架Glide
- javascript制作照片墙及制作过程中出现的问题
- Android图片的三级缓存
- 总结的一些android公共库
- LruCache,DiskLruCache实现相关研究记录
- 收藏各种技术源码
- iOS 基础知识大全之网络篇(可供零基础学习)
- javascript制作照片墙及制作过程中出现的问题
- android三步处理图片缓存的方法
- Android DiskLruCache完全解析,硬盘缓存的最佳方案
- android listview 异步加载图片并防止错位
- LruMemoryCache和LruCache
- 10分钟教你学会三级缓存
- 一个简单的Volley图片二级缓存
- Android异步加载全解析之引入一级缓存