CWAC Prefs: For When Regular Preferences Just Aren't Enough
SharedPreferences are persisted in an XML file, in the internal storage
of your app. In many cases, this is perfectly fine. In other cases,
though, you might want to store those preferences in some other container.
This project provides another implementation of the
interface, one where you can supply a strategy object to define how
the values should be loaded and persisted. Built-in strategies support
storing the values in SQLite, or encrypted in SQLCipher for Android.
This is available as a debug JAR file from the downloads area of this GitHub repo. Note that if you wish to use the JAR and use the encrypted preferences, you will also need a compatible edition of SQLCipher for Android.
The project itself is set up as an Android library project, in case you wish to use the source code in that fashion, though then you will also need SQLCipher for Android.
Most of your usage of the
CWSharedPreferences class (in
will be the same as how you would use
CWSharedPreferences implements that interface. Hence, you still use
getInt() to retrieve preference values,
edit() to get an
with setters like
putInt(), and so on.
What differs is how you get the
CWSharedPreferences object in the first place. You
will not get one from the standard
PreferenceManager or via some method on
Rather, you will use a
getInstance() method on the
CWSharedPreferences class, supplying
a strategy object to use if the
CWSharedPreferences object does not yet exist. As with
CWSharedPreferences are held onto in a static data member and will
live for the life of your process, treated as a singleton on a per-name basis.
Defining and Using a Strategy
getInstance() method on
CWSharedPreferences, to give you a
object, takes a
CWSharedPreferences.StorageStrategy object as a parameter. This interface
defines where and how the preference values are loaded and stored.
There are two standard implementations of
SQLiteStrategy, which stores its data in a SQLite database
SQLCipherStrategy, which stores its data in a SQLCipher for Android encrypted database
The constructor for
SQLiteStrategy takes three parameters:
Contextobject for use with an internal
- a string representing the name of the database file to use (also serves as a unique key for
the static cache of
LoadPolicy, described in greater detail below
The constructor for
SQLCipherStrategy takes those parameters, plus the password for the
database. This password will be used to encrypt the database if this is no existing database,
plus it is used to decrypt the database as needed over time.
LoadPolicy indicates what should happen when the
CWSharedPreferences is created
and needs to load its data. There are three possible
LoadPolicy enum values:
SYNCmeans that the
CWSharedPreferenceswill load its in-memory cache of the preference values on the current thread. Use this if you are using your own background thread to initialize the
CWSharedPreferencesinstance and do not need
CWSharedPreferencesto fork its own thread for this purpose.
ASYNC_BLOCKis the same basic behavior you see with the stock implementation of
SharedPreferences: the data is loaded in a background thread, but your attempt to read that data blocks until the load is complete.
ASYNC_EXCEPTIONwill throw a
RuntimeExceptionif you attempt to read from a
CWSharedPreferenceswhere the load has not yet completed.
SYNC is generally the best answer, as you are assured that you will not block
the main application thread.
ASYNC_BLOCK could block the main application
thread if you try using the preferences right away and they have not been loaded
So, for example, you could create a
SQLiteStrategy object via:
new SQLiteStrategy(getContext(), NAME, LoadPolicy.SYNC);
(assuming a static data member named
NAME that is the database name, and
assuming the existence of some
getContext() method to return a
Then, you could retrieve a
CWSharedPreferences.getInstance(new SQLiteStrategy(getContext(), NAME, LoadPolicy.SYNC));
Due to some unfortunate limitations
in the way
PreferenceActivity work, there is no known
simple way to use
CWSharedPreferences with these classes.
To use the project with
SQLCipherStrategy, or to build the project
from source (including using it as an Android library project), you
will need to download and install SQLCipher for Android. The library
project already has a compatible edition, so if you clone the repo and
use the library project in situ, you should be OK. If you wish to
use the JAR instead, visit the SQLCipher for Android
Web site to learn more about how to add it to your project.
Other than that, there are no dependencies for the main project.
Notably, if you are using the JAR and not using
you should not need SQLCipher for Android.
This is version v0.0.1 of this module, meaning that this is a proof of concept, seeking input from interested parties.
Normally, CWAC projects have a
demo/ subproject. This one does not.
In part, that is due to the UI limitations mentioned above.
It does, however, have a
tests/ sub-project that implements a JUnit
test suite for exercising basic aspects of the API. Note, though, that
these tests are "white box", insofar as the tests live in the same
CWSharedPreferences and access package-private methods.
The code in this project is licensed under the Apache Software License 2.0, per the terms of the included LICENSE file.
Do not ask for help via Twitter.
And, for those of you who skipped over that sentence: do not ask for help on Twitter. Anyone who thinks that developer support can be handled in 140-character chunks should not be attempting to use a CWAC component.
Now, that being said, the rest of your help will be for bugs or questions.
If you are experiencing some sort of problem using this component, where you are fairly certain the component
itself is at fault, please submit a pull request
for a new test case to be added to the
tests/ project that demonstrates the problem. Or, create
a sample project that demonstrates the problem, post the source code to
that project somewhere (e.g., a public GitHub repo), and file an
issue, pointing to your project and providing
instructions on how to reproduce the problem.
Do not file an issue if you cannot reproduce the problem, or with only partial source code that may or may not be related to the problem.
Feature Requests and Non-Bug Issues
If you see likely flaws just by looking at the code, or you see places where we would really need additional stuff for this to be useful, please file an issue.
If you have questions regarding the use of this code, please post a question
on StackOverflow tagged with
android. Be sure to indicate
what CWAC module you are having issues with, and be sure to include relevant source code
and stack traces if you are encountering crashes.
- v0.0.1: Initial import