您的位置:首页 > 移动开发 > Android开发

Android照片墙完整版,完美结合LruCache和DiskLruCache

2016-06-23 19:46 766 查看
实现效果:



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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息