An annotation processor for type-safe access to shared preferences.
Download the latest jar or use it as a dependency via Gradle
compileOnly 'eu.jonahbauer:android-preference-annotations:1.0.0'
annotationProcessor 'eu.jonahbauer:android-preference-annotations:1.0.0'
or Gradle Kotlin DSL
compileOnly("eu.jonahbauer:android-preference-annotations:1.0.0")
annotationProcessor("eu.jonahbauer:android-preference-annotations:1.0.0")
This annotation processor requires the Android SDK to be present on the classpath and a java version of at least 11.
To make use of this annotation processor simply annotate a class with the @Preferences
annotation
and define your preferences therein:
@Preferences(name = "org.example.AppPreferences$Generated", r = R.class, value = {
@PreferenceGroup(name = "general", prefix = "preferences_general_", suffix = "_key", value = {
@Preference(name = "boolean_pref", type = boolean.class),
@Preference(name = "byte_pref", type = byte.class),
@Preference(name = "short_pref", type = short.class),
@Preference(name = "char_pref", type = char.class),
@Preference(name = "int_pref", type = int.class),
@Preference(name = "long_pref", type = long.class),
@Preference(name = "float_pref", type = float.class),
@Preference(name = "double_pref", type = double.class),
@Preference(name = "string_pref", type = String.class),
@Preference(name = "string_set_pref", type = Set.class),
@Preference(name = "void_pref", type = void.class)
})
})
public final class AppPreferences extends AppPreferences$Generated {}
This will generate a class org.example.AppPreferences$Generated
which contains accessors
for all the specified preferences (except void
preferences). Additionally, an accessor for
the preference keys is generated.
Before you can actually access any shared preferences you first have to tell the generated class which
SharedPreferences
to use. This can easily be done in the applications onCreate
method:
public void onCreate() {
// ...
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
AppPreferences.init(preferences, this.getResources());
// ...
}
You can access the preferences from anywhere in the application without requiring a Context
:
AppPreferences.general().intPref() // returns the default value of 0
AppPreferences.general().intPref(42) // sets the preference R.string.preferences_general_int_pref_key to 42
AppPreferences.general().intPref() // returns the newly assigned value of 42
AppPreferences.general().keys().intPref() // returns the string resource R.string.preferences_general_int_pref_key
If necessary you can also disable the generation of fluent getters and setters by setting
fluent = false
on the @Preferences
annotation, which will
help with Kotlin interoperability:
AppPreferences.getGeneral().getIntPref()
AppPreferences.getGeneral().setIntPref(42)
AppPreferences.getGeneral().getKeys().getIntPref()
or in Kotlin
AppPreferences.general.intPref
AppPreferences.general.intPref = 42
AppPreferences.general.keys.intPref
Furthermore, you can enable generation of Editor
classes by setting editor = true
on the @Preferences
annotation:
AppPreferences.general().edit()
.intPref(42)
.stringPref("Hello World!")
.apply()
By default, boolean
, byte
, short
, char
, int
, long
, float
, double
, String
, void
, Set<String>
and
enums are supported (to declare a string set preference just use Set.class
).
Since SharedPreferences
don't support all of these types natively some special handling is required:
type | storage format |
---|---|
boolean , int , long , float , String , Set<String> |
natively supported |
byte , short , char |
stored as int |
double |
stored as long via Double.longBitsToDouble and Double.doubleToRawLongBits |
enum |
stored as String via Enum.name and Enum.valueOf |
void |
no accessors generated |
Other types may be used by specifying a custom serializer that will convert between the preference
type and one of the supported types (except void
and enum
):
@Preference(name = "bean_pref", type = Bean.class, serializer = JsonBeanSerializer.class)
@Data
public class Bean {
private String foo;
private String bar;
}
public class JsonBeanSerializer<T> implements PreferenceSerializer<T, String> {
private final Class<? extends T> clazz;
private final ObjectMapper mapper = new ObjectMapper();
public JsonBeanSerializer(Class<? extends T> clazz) {
this.clazz = clazz;
}
@SneakyThrows
public T deserialize(String value) {
if (value == null) return null;
return mapper.readValue(value, clazz);
}
@SneakyThrows
public String serialize(T value) {
if (value == null) return null;
return mapper.writeValueAsString(value);
}
}
Since the generated class can be initialized with any SharedPreferences
implementation, you can easily
provide an instance of EncryptedSharedPreferences
in order to encrypt your preferences.
Find a bug or want to request a new feature? Please let us know by submitting an issue.