FIFOCache: a self-managing cache for Android

By Anas Ambri

The Scalable Capital Android app allows its users to download and view a wide range of documents on their smartphone; from the initial contractual information to the regular invoices and transfer statements. Accessibility to all these documents is central to maintaining our promise of transparency to our clients. On the other hand, providing these files on a mobile phone comes with its own set of challenges. First, downloading large amounts of data over mobile networks comes at a price to the consumer. Second, all the downloaded files end up on the user’s device, bloating the size of the app, and prompting many users to delete the app altogether.

Ultimately, when attempting to provide remote files for viewing inside an app, two things should be kept in mind:

  • Only download files that have not been downloaded before. While this might seem obvious, it suggests using some caching strategy, which comes with its own problem of cache invalidation.
  • Limit the overall total size of downloaded files associated with one’s app, so as to never be the target of a user’s cleaning spree.

Indeed, this is the strategy that we follow in our app. And today, we would like to show how we do it. In the same vein, this will be a chance to demonstrate one of Scalable Capital’s open-source projects: FIFOCache. (The repository contains a sample app, which will be used in the rest of the article).

What is FIFOCache?

As the name suggests, it’s a first-in, first-out cache that uses an app’s internal cache directory to store provided files (in the form of input streams), and retrieves them based on a key. Its usage is as simple as:

public class MainActivity extends AppCompatActivity {
    @Override
protected void onCreate(Bundle savedInstanceState) {
//...
FIFOCache cache = new FIFOCache(this);

InputStream inputStream = getAssets().open("sample.pdf");
long size = getAssets().openFd("sample.pdf").getLength();
cache.cache(inputStream, "sample", size);
//...
File cachedFile = cache.retrieve("sample");
}
}

As long as the cache is never cleared, the file can always be retrieved under the provided file name.

Now, for the more interesting stuff: remember how we needed a way to limit the total size of our downloaded files? It turns out that it is as simple as:

cache.setSize(4 * 1024 * 1024); //4 MB

This will ensure that the cache never goes beyond 4MB. Anytime a new input stream might be added, we clear just enough files in the cache to fit in the new one, starting from the oldest files in the cache.

By default, the size of the cache is 5MB.

Where do the files live?

All the files are kept under the app’s internal cache directory. This is the ideal location because:

  • The files will be automatically deleted when the app is uninstalled, ensuring that confidential files are never left in a device inadvertently.
  • Living in a private folder ensures that the cached files cannot be manipulated by other apps.

In practice, the files will live under a subdirectory of the cache folder. To change this location, simply use the method:

cache.setSubdirectory("hidden");

Wait, I’ve seen this before…

If you’ve used DiskLruCache before, then you’ll notice that this library tackles a smaller subset of the former’s capabilities. However, here are some reasons why you’d want to use FIFOCache instead:

  • Simpler API: as opposed to dealing with an Editor interface, all you need to know are cache/retrieve.
  • Geared for Android: FIFOCache makes many assumptions on how to deal with files, especially when it comes to the directory where they are stored.
  • Tested to run on Android.

Conclusion

FIFOCache is a simple abstraction which tries to simplify the process of caching remote files on a user’s device. In a future article, we will see how it can be combined with other components to improve Android’s Download Manager service.

Thanks to Asad Shah for reviewing drafts of this article