4

I'm trying to use Jake Wharton's DiskLruCache in my Android app, but I can't seem to figure out how to properly serialize and deserialize objects using the cache. Using the following code in a basic command line Java program:

DiskLruCache.Editor editor = null;
try {
    editor = diskLruCache.edit("objects");

    OutputStream timeOs = editor.newOutputStream(0);
    OutputStream dataOs = editor.newOutputStream(1);
    OutputStream timeBos = new BufferedOutputStream(timeOs);
    OutputStream dataBos = new BufferedOutputStream(dataOs);
    ObjectOutputStream timeOos = new ObjectOutputStream(timeBos);
    ObjectOutputStream dataOos = new ObjectOutputStream(dataBos);

    long createTime = System.currentTimeMillis();

    String str = "testString";

    ArrayList<String> list = new ArrayList<String>();
    list.add("item1");
    list.add("item2");

    timeOos.writeLong(createTime);

    // this works:
    dataOos.writeObject(str);
    // this does not work:
    //dataOos.writeObject(list);

    timeOos.close();
    dataOos.close();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (editor != null)
        try {
            editor.commit();
        } catch (IOException e) {
            e.printStackTrace();
        }
}

timeOos.writeLong(createTime) and dataOos.writeObject(str) successfully write data to the cache, but replacing dataOos.writeObject(str) with dataOos.writeObject(list) does not work. I have tried ArrayLists and HashMaps, and it appears that these are not serialized and written to the cache. The program executes all of the code, then hangs for around a minute before returning, leaving only the journal file in the cache directory.

I'm not sure if this would be an issue with DiskLruCache being unable to handle container objects.

The full source and original post can be found here

EDIT (2014-01-03): Here's a JUnit test using the Android SDK. testStoreLong(), testStoreString(), and testStoreArrayList() pass but testPersistArrayListSnapshot() and testPersistArrayListEditor() fail.

It's a strange issue; if I put a breakpoint at line 101 (editor.commit();) then step over, the cache file test-store-array-list.0 is not created and snapshot == null, failing the test. But if I put a breakpoint at line 103 (DiskLruCache.Snapshot snapshot = mDiskLruCache.get("test-store-array-list");) the file is created as expected.

Perhaps there's a bug in DiskLruCache; are there any alternative disk caching libraries that are Android-compatible?

Spencer
  • 1,161
  • 12
  • 19

2 Answers2

3

Hmmmm yeah mad props to Jake Wharton for creating this caching library but I found it very unintuitive to use, all the examples I've found are for image caching.

I've recreated two methods that should help you

 public void put(String key, Object object)
    {
        DiskLruCache.Editor editor = null;
        try
        {
            editor = mDiskCache.edit(key);
            if (editor == null)
            {
            return;
            }

            ObjectOutputStream out = new ObjectOutputStream(editor.newOutputStream(0));
            out.writeObject(object);
            out.close();
            editor.commit()
        }
catch()...etc

and for getting your objects out again

    public Object get(String key)
    {
       DiskLruCache.Snapshot snapshot;

       try
       {
            snapshot = mDiskCache.get(key);
            ObjectInputStream in = new ObjectInputStream(snapshot.getInputStream(0));
            return (Object) in.readObject();

        }
        catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        catch (ClassNotFoundException ex)
        {
            ex.printStackTrace();
        }
    }

These are very basic get and put methods your example code seems to be bit complicated for putting objects in the cache this I think might be the issue. After you call the get() method simply cast the object to whatever type you want or better still change these methods to use generics for type safety.

Hevski
  • 1,821
  • 1
  • 17
  • 26
  • Thanks, but unfortunately I'm still having issues with `ArrayList`s. See my most recent edit. – Spencer Jan 03 '14 at 20:11
  • Did you try my functions? – Hevski Jan 03 '14 at 20:47
  • Yes (https://gist.github.com/elliottsj/8246499). A similar problem occurs, where `snapshot == null` (on line 37 of MyCache.java) – Spencer Jan 03 '14 at 21:05
  • OK, I have no issues with storing the ArrayLists in my App, I'll run your code sometime tomorrow and get back to you. – Hevski Jan 03 '14 at 21:47
  • just had a quick look at the code, line 35 of myCacheTest 'myCache = new MyCache(mDirectory);'. I'm not sure but this might be re-creating and overwriting the existing cache, I think there is an open method somthing like mDiskLruCache = DiskLruCache.open(cacheDir, DISK_CACHE_SIZE); – Hevski Jan 03 '14 at 21:52
  • Check if out, if cache already exists http://stackoverflow.com/questions/16054007/how-check-if-a-disklrucache-already-exists-android – Hevski Jan 03 '14 at 22:01
  • I think the issue is that the cache file `test-array-list.0` is not being written in the first place. I just tried putting `myCache.put("test-string", "hello");` in a new test case and removed `myCache.mDiskLruCache.delete();` from the tearDown, and the file `test-string.0` is written successfully into the cache directory, but not `test-array-list.0`. – Spencer Jan 03 '14 at 22:55
3

The problem was that the cache size was too small.

I solved the issue by changing this:

diskLruCache = DiskLruCache.open(new File("DiskLruCache"), 1, 2, 20 * 2^20);

To this:

diskLruCache = DiskLruCache.open(new File("DiskLruCache"), 1, 2, (long) (20 * Math.pow(2, 20)));

^ is the bitwise exclusive OR operator, not the exponent operator so the cache size was actually 20 * 2^20 == 60 instead of what it should have been: 20 * Math.pow(2, 20) == 20971520.

Spencer
  • 1,161
  • 12
  • 19