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

阅读全文
0 0