LruCache、DiskLruCache实现自定义SharedPreferences
来源:互联网 发布:阿里云linux服务器搭建 编辑:程序博客网 时间:2024/06/04 21:16
8月份新上了一个项目,涉及到多用户下的数据共享,Android手机的默认用户id是0,也就是我们平时使用的用户,当进入到其他用户时,虽然是同一app,但是处于不同的用户下,通过SharedPreferences共享数据肯定是做不到的,而且SharedPreferences不能存储Bitmap,而在项目中,需要用到视频的第一帧图片,综合考虑之后,决定在公共目录下通过写文件的形式存储Bitmap等数据。本着代码复用的原则,决定通过LruCache和DiskLruCache自定义一个具备二级缓存的加强版的SharedPreferences。
ps:在这里就不介绍LruCache和DiskLruCache的具体用法了,网上可以找到很多详细的解析。
首先定义一个操作类公共的接口:
public interface ICache { void put(String key, Object value); void remove(String key) throws IOException; void clear() throws IOException; Object getObject(String key); int getInt(String key); long getLong(String key); double getDouble(String key); float getFloat(String key); boolean getBoolean(String key); Bitmap getBitmap(String key); String getString(String key); byte[] getBytes(String key);}
然后定义分别定义MemoryCache和DiskCache去实现ICache接口,采用LruCache、DiskLruCache来实现一二级缓存。
/** * This class uses {@link LruCache} to cache data and does not allow null * to be used as a key or value. The more information can be found on {@link LruCache}. * * Created by Feng Bangquan on 17-11-11 */public class MemoryCache implements ICache { private LruCache<Object, Object> mLruCache; /** * @param maxMemorySize this is the maximum sum of the sizes of the entries * in this cache. */ public MemoryCache(int maxMemorySize){ mLruCache = new LruCache<>(maxMemorySize); } /** * Caches {@code value} for {@code key} calling {@link LruCache#put(Object, Object)} */ @Override public void put(String key, Object value) { mLruCache.put(key, value); } /** * Removes the entry for {@code key} if it exists. */ @Override public void remove(String key) { mLruCache.remove(key); } /** * Clear the MemoryCache, calling {@link LruCache#evictAll()} */ @Override public void clear() { mLruCache.evictAll(); } /** * Returns the value for {@code key} if it exists in the cache . * The following methods call {@link #getObject(String)} * to get the specific types' value: * @see #getInt(String) * @see #getBitmap(String) * @see #getBoolean(String) * @see #getBytes(String) * @see #getDouble(String) * @see #getFloat(String) * @see #getLong(String) * @see #getString(String) */ @Override public Object getObject(String key) { return mLruCache.get(key); } @Override public int getInt(String key) { Object object = mLruCache.get(key); return object == null ? null : (int) object; } @Override public long getLong(String key) { Object object = mLruCache.get(key); return object == null ? null : (long) object; } @Override public double getDouble(String key) { Object object = mLruCache.get(key); return object == null ? null : (double) object; } @Override public float getFloat(String key) { Object object = mLruCache.get(key); return object == null ? null : (float) object; } @Override public boolean getBoolean(String key) { Object object = mLruCache.get(key); return object == null ? null : (boolean) object; } @Override public Bitmap getBitmap(String key) { Object object = mLruCache.get(key); return object == null ? null : (Bitmap) object; } @Override public String getString(String key) { Object object = mLruCache.get(key); return object == null ? null : String.valueOf(object); } @Override public byte[] getBytes(String key) { Object object = mLruCache.get(key); return object == null ? null : (byte[]) object; }}
/** * This class uses {@link DiskLruCache} to cache data and does not allow null * to be used as a key or value. The more information can be found on {@link DiskLruCache}. * * Created by Feng Bangquan on 17-11-11 */public class DiskCache implements ICache { private DiskLruCache mDiskLruCache; /** * Opens the cache in {@code directory}, creating a cache if none exists * there. * * @param directory a writable directory * @param appVersion * @param valueCount the number of values per cache entry. Must be positive. * @param maxSize the maximum number of bytes this cache should use to store * @throws IOException if reading or writing the cache directory fails */ public DiskCache(File directory, int appVersion, int valueCount, long maxSize) throws IOException { mDiskLruCache = DiskLruCache.open(directory, appVersion, valueCount, maxSize); } /** * Caches {@code value} for {@code key} in {@code directory} */ @Override public void put(String key, Object value) { try { DiskLruCache.Editor editor = mDiskLruCache.edit(key); OutputStream outputStream = editor.newOutputStream(0); ObjectOutputStream objectOutputStream; if (value instanceof Bitmap) { outputStream.write(bitmapToBytes((Bitmap)value)); } else if (value instanceof byte[]) { outputStream.write((byte[]) value); } else { objectOutputStream = new ObjectOutputStream(outputStream); objectOutputStream.writeObject(value); objectOutputStream.close(); } outputStream.close(); editor.commit(); mDiskLruCache.flush(); } catch (IOException e) { e.printStackTrace(); } } /** * Calls {@link DiskLruCache#remove(String)} to drop the entry for {@code key} * if it exists and can be removed. */ @Override public void remove(String key) throws IOException { mDiskLruCache.remove(key); } /** * Calls {@link DiskLruCache#delete()} to close the cache and deletes all of its stored values. * This will delete all files in the cache directory including files that weren't created by * the cache. */ @Override public void clear() throws IOException { mDiskLruCache.delete(); } /** * Returns an object named {@code key}, or null if it doesn't exist is not currently readable. * The following methods call {@link #getObject(String)} to get the specific types' value: * @see #getInt(String) * @see #getLong(String) * @see #getDouble(String) * @see #getFloat(String) * @see #getBoolean(String) * @return Object */ @Override public Object getObject(String key) { try { DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key); if (snapshot != null) { ObjectInputStream objectInputStream = new ObjectInputStream(snapshot.getInputStream(0)); return objectInputStream.readObject(); } else { return null; } } catch (Exception e) { e.printStackTrace(); return null; } } @Override public int getInt(String key) { Object object = getObject(key); return object == null ? null : (Integer) object; } @Override public long getLong(String key) { Object object = getObject(key); return object == null ? null : (Long) object; } @Override public double getDouble(String key) { Object object = getObject(key); return object == null ? null : (Double) object; } @Override public float getFloat(String key) { Object object = getObject(key); return object == null ? null : (Float) object; } @Override public boolean getBoolean(String key) { Object object = getObject(key); return object == null ? null : (Boolean) object; } /** * Returns a Bitmap named {@code key}, or null if it doesn't exist is not currently readable. * @return Bitmap */ @Override public Bitmap getBitmap(String key) { try { DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); InputStream inputStream = snapShot.getInputStream(0); Bitmap bitmap = BitmapFactory.decodeStream(inputStream); inputStream.close(); return bitmap == null ? null : bitmap; } catch (IOException e) { e.printStackTrace(); return null; } catch (NullPointerException e) { e.printStackTrace(); return null; } } @Override public String getString(String key) { Object object = getObject(key); return object == null ? null : (String)object; } /** * Returns bytes[] named {@code key}, or null if it doesn't exist is not currently readable. * @return */ @Override public byte[] getBytes(String key) { try { DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key); return snapshot == null ? null : streamToBytes(snapshot.getInputStream(0)); } catch (IOException e) { e.printStackTrace(); return null; } } /** * Transforms Bitmap to bytes[] * @param bitmap the Bitmap to be transformed * @return a byte[] transforms from Bitmap */ private byte[] bitmapToBytes(Bitmap bitmap) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream); try { byteArrayOutputStream.flush(); byteArrayOutputStream.close(); if (!bitmap.isRecycled()){ bitmap.recycle(); } } catch (IOException e) { e.printStackTrace(); } return byteArrayOutputStream.toByteArray(); } /** * Transforms InputStream to byte[] * @param in the InputStream to be transformed * @return a byte[] transforms from InputStream */ private byte[] streamToBytes(InputStream in) { ByteArrayOutputStream out = new ByteArrayOutputStream(); int length; byte[] bytes = new byte[1024]; try { while ((length = in.read(bytes)) != -1) { out.write(bytes, 0, length); } in.close(); out.close(); } catch (IOException e) { e.printStackTrace(); return null; } return out.toByteArray(); }}
最后在Cache类中分别实例化MemoryCache和DiskCache,Cache.open()传入DiskCache的存储路径以及其它初始化参数,再通过putXXX(key, value)和getXXX(key, value)即可实现对数据的存储和读取。
public class Cache { private static MemoryCache mMemoryCache; private static DiskCache mDiskCache; /** * Opens the cache in {@code directory}, creating a cache if none exists there. * * @param directory a writable directory * @param appVersion the value must be positive * @param valueCount the number of values per cache entry. Must be positive. * @param maxDiskSize the maximum number of bytes this diskCache should use to store * @param maxMemorySize the maximum number of kilobytes this memoryCache should be to store * @throws IOException if reading or writing the cache directory fails */ public static void open(File directory, int appVersion, int valueCount, long maxDiskSize, int maxMemorySize) throws IOException { mMemoryCache = new MemoryCache(maxMemorySize); mDiskCache = new DiskCache(directory, appVersion, valueCount, maxDiskSize); } /** * Removes the entry for {@code key} if it exists. * @throws IOException */ public static void remove(String key) throws IOException { mMemoryCache.remove(key); mDiskCache.remove(key); } /** * Closes the cache and deletes all of its stored values. This will delete * all files in the cache directory including files that weren't created by * the cache. * @throws IOException */ public static void clear() throws IOException { mMemoryCache.clear(); mDiskCache.clear(); } /** * Caches {@code value} for {@code key} calling {@link LruCache#put(Object, Object)} * and {@link DiskCache#put(String, Object)}. The following methods putXXX(key, value) * cache the specific types' data. * @param key the value of key can not contain (" ") , ("\n"), ("\r") * @param value it does not support generic types */ public static void put(String key, Object value) { mMemoryCache.put(key, value); mDiskCache.put(key, value); } public static void putString(String key, String value){ put(key, value); } public static void putInt(String key, int value) { put(key, value); } public static void putFloat(String key, float value) { put(key, value); } public static void putDouble(String key, double value){ put(key, value); } public static void putBytes(String key, byte[] value) { put(key, value); } public static void putBoolean(String key, boolean value) { put(key, value); } public static void putBitmap(String key, Bitmap value) { put(key, value); } /** * Returns the value for {@code key} if it exists in the MemoryCache or return * the value if it exists in the DiskCache, otherwise return null. The following * method getXXX(key, value) return specific types' value. * @param key * @return */ public static Object getObject(String key) { if (mMemoryCache.getObject(key) != null) { return mMemoryCache.getObject(key); } else { return mDiskCache.getObject(key); } } public static int getInt(String key) { if (mMemoryCache.getObject(key) != null) { return mMemoryCache.getInt(key); } else { return mDiskCache.getInt(key); } } public static long getLong(String key) { if (mMemoryCache.getObject(key) != null) { return mMemoryCache.getLong(key); } else { return mDiskCache.getLong(key); } } public static double getDouble(String key) { if (mMemoryCache.getObject(key) != null) { return mMemoryCache.getDouble(key); } else { return mDiskCache.getDouble(key); } } public static float getFloat(String key) { if (mMemoryCache.getObject(key) != null) { return mMemoryCache.getFloat(key); } else { return mDiskCache.getFloat(key); } } public static boolean getBoolean(String key) { if (mMemoryCache.getObject(key) != null) { return mMemoryCache.getBoolean(key); } else { return mDiskCache.getBoolean(key); } } public static Bitmap getBitmap(String key) { Bitmap bitmap = mMemoryCache.getBitmap(key); if (bitmap != null && !bitmap.isRecycled()) { return bitmap; } else { return mDiskCache.getBitmap(key); } } public static String getString(String key) { if (mMemoryCache.getString(key) != null) { return mMemoryCache.getString(key); } else { return mDiskCache.getString(key); } } public static byte[] getBytes(String key) { if (mMemoryCache.getBytes(key) != null) { return mMemoryCache.getBytes(key); } else { return mDiskCache.getBytes(key); } }}
下面是一个测试用例:
/** * A demo activity to guide how to use Cache to store data */public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private static final String KEY_INT = "int"; private static final String KEY_STRING = "string"; private static final String KEY_BOOLEAN = "boolean"; private static final String KEY_BITMAP = "bitmap"; private static final String KEY_BYTES = "bytes"; private static final long DISK_CACHE_SIZE = 1024 * 1024 * 10; // 100MB @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final int maxMemorySize = (int) (Runtime.getRuntime().maxMemory() / 1024); try { File cacheDir = getDiskCacheDir("mySharedPreferences"); if (!cacheDir.exists()) { cacheDir.mkdirs(); } Cache.open(cacheDir, 1, 1, DISK_CACHE_SIZE, (maxMemorySize / 8)); } catch (IOException e) { e.printStackTrace(); } (findViewById(R.id.put)).setOnClickListener(this); (findViewById(R.id.get)).setOnClickListener(this); (findViewById(R.id.remove)).setOnClickListener(this); (findViewById(R.id.clear)).setOnClickListener(this); } public File getDiskCacheDir(String uniqueName) { String cachePath; if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) { cachePath = getExternalCacheDir().getPath(); } else { cachePath = getCacheDir().getPath(); } return new File(cachePath + File.separator + uniqueName); } private void putCache() { new Thread(new Runnable() { @Override public void run() { BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 8; Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.picture, options); Cache.putBitmap(KEY_BITMAP, bitmap); Cache.putInt(KEY_INT, 1024); Cache.putString(KEY_STRING, "read the source code"); Cache.putBoolean(KEY_BOOLEAN, true); } }).start(); } private void getCache() { Bitmap bitmap = Cache.getBitmap(KEY_BITMAP); ((ImageView) findViewById(R.id.image_view)).setImageBitmap(bitmap); System.out.println("getInt: " + Cache.getInt(KEY_INT)); System.out.println("getString: " + Cache.getString(KEY_STRING)); System.out.println("getBoolean: " + Cache.getBoolean(KEY_BOOLEAN)); } @Override public void onClick(View v) { if (v.getId() == R.id.put) { putCache(); } if (v.getId() == R.id.get) { getCache(); } if (v.getId() == R.id.clear) { try { Cache.clear(); } catch (IOException e) { e.printStackTrace(); } } if (v.getId() == R.id.remove) { try { Cache.remove(KEY_INT); Cache.remove(KEY_BYTES); Cache.remove(KEY_BOOLEAN); Cache.remove(KEY_BITMAP); Cache.remove(KEY_BYTES); } catch (IOException e) { e.printStackTrace(); } } }}
ImageView上显示出了照片,说明bitmap能够正确存储和读取:
01-04 13:08:30.529 17505-17505/com.fengbangquan.cache I/System.out: getInt: 102401-04 13:08:30.534 17505-17505/com.fengbangquan.cache I/System.out: getString: read the source code01-04 13:08:30.544 17505-17505/com.fengbangquan.cache I/System.out: getBoolean: true
这样,具有二级缓存,能够存储Bitmap的自定义SharedPreferences就完成了,需要注意的是Cache在多线程的操作不是线程安全的。完整代码下载地址:https://github.com/fengbangquan/Cache
- LruCache、DiskLruCache实现自定义SharedPreferences
- LruCache,DiskLruCache实现分析
- LruCache和DiskLruCache实现二级缓存的自定义ImageLoader
- 使用lrucache和diskLrucache实现照片墙
- 使用LruCache和DiskLruCache实现内存磁盘二级图片缓存
- 使用LruCache和DiskLruCache实现ListView双缓存
- 利用LruCache和DiskLruCache实现图片异步下载
- 使用LruCache和DiskLruCache实现ListView双缓存
- 完美结合LruCache和DiskLruCache实现Android照片墙
- Android使用LruCache、DiskLruCache实现图片缓存+图片瀑布流
- AndroidStudio利用DiskLruCache和LruCache实现简单的照片墙
- LruCache和DiskLruCache
- 图片缓存-LruCache、DiskLruCache
- Bitmap的缓存(LruCache,DiskLruCache)
- Fragment中存放GridView,结合LruCache、DiskLruCache和HttpURLConnection实现图片下载、缓存
- 利用LruCache和DiskLruCache加载网络图片实现图片瀑布流效果(升级版)
- 实现自己的ImageLoader(2)-----LruCache与DiskLruCache缓存详解
- Android相册,利用LruCache、DiskLruCache实现图片的加载,避免出现OOM
- 图像处理中的forward warping 和 inverse warping
- 用android studio写一个简单并且bug奇多的计算器
- eclipse常用快捷键汇总
- python ftplib模块
- [水文]Mask Rcnn简要阅读笔记
- LruCache、DiskLruCache实现自定义SharedPreferences
- java正则表达式实现简单词法分析
- LightOJ1079 Just another Robbery 01背包、概率
- Java之应用ACM OJ
- (源码)详细分析Android中的引用机制Reference(WeakReference、SoftReference、PhantomReference)
- 顺序栈实现迷宫寻径
- c语言:普里姆算法和克鲁斯卡尔算法求最小生成树
- springData
- 关于Oracle数据库起别名