ENUM: best way to handle Android settings.

                          As a JAVA/Android Developer, I am curious about how most of the things work in JAVA and i started reading ORACLE JAVA Tutorial and stumbled upon the ENUM datatype tutorial page. Since school, I know ENUM is a special data-type which can hold specific set of constants. In C++  we can set integer values to these constants.

For eg:-  We can have a set of constants called DAY using

In C++ :
  enum day {SUN=1,MON,TUE,WED,THU,FRI,SAT}; 
 
where SUN has value 1, rest is incremented by 1 and so on.

In JAVA:
  enum Day {SUN,MON,TUE,WED,THU,FRI,SAT}; 
 
But you cannot set values to these constants. It is like, Day is a static class with predefine static variables with no values SUN, MON, etc. Any one can access these constants without creating an Object/Instance of this static class, for eg: Day.SUN.

                        By lurking around the same ENUM tutorial page I found that ENUM datatype can be used just like a normal Class in JAVA with some exceptions(need private/ default access for constructor). We can extend one class to the ENUM. for eg:- Iwe have Class A, we can extend Class A into ENUM just like we do:

public enum Day extends A{}  

                        And what's more interesting is that each constant in the ENUM class is like a static object/instance of that ENUM class. It can have constructors with predefined values as arguments. These arguments can be stored as a member variable inside the ENUM class. Just look into the following example, and you will be surprised (of-course, if  you didn't know this before).

public enum Planet {
    MERCURY (3.303e+23, 2.4397e6),
    VENUS   (4.869e+24, 6.0518e6),
    EARTH   (5.976e+24, 6.37814e6),
    MARS    (6.421e+23, 3.3972e6),
    JUPITER (1.9e+27,   7.1492e7),
    SATURN  (5.688e+26, 6.0268e7),
    URANUS  (8.686e+25, 2.5559e7),
    NEPTUNE (1.024e+26, 2.4746e7);

    public final double mass;   // in kilograms
    public final double radius; // in meters
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
} 

Each Planet, MERCURY, VENUS, ... is a static instance of ENUM class Planet with two variable members mass and radius. And we can find the radius of EARTH just by calling Planet.EARTH.radius. So simple, isn't it. Well this simple/interesting feature made me re-think to rewrite the settings code in one of the Android application i was working on.

                           Most of the Android applications have simple settings page where we handle the over-all settings of the app (for eg: themes, colors, text, font etc..).  And most of these settings are applicable to the whole app, such that it needs to be accessed / handled (read/write settings) anywhere inside the code. Android provides one good way of doing it using Preferences. We can use PreferenceActivity to handle settings in our android application, where each settings is stored as Preference in Shared Preference. And it can be accessed anywhere within in the application, only we need is the "key" for the preference and "context" instance. For eg:


Settings page in Shouter/Notification Reader App
Above screenshot is a simple setting page, where there are some checkbox settings, button click settings and more. Enable settings is a CheckBoxPreference (its a checkbox widget with preference functionality) where checked state will  be saved in a specified preference when user clicks on it. Similarly there are so many other settings or preferences. And you have to read these preference every time when some trigger occurs.

For example above Shouter app shouts the content from a notification when it comes. So every time when a new notification comes, developer will be checking for the setting state "enable", whether shouting is enabled or not, using preference. Every time, either developer will be checking preference And there will be lots of settings like that and he/she should remember the "key" of these preferences. This is cumbersome for the developer to remember preference key for each settings and handle code.

One way to avoid these remembering problem is by using public static methods. For eg:-

public static boolean isShoutingEnabled(){
     boolean enabled = /* get value from preference */;
     return boolean;
} 

Of-course, this will solve the problem. But suppose you have lots of settings, more than 10/20/30, you have to right 10/20/30 static methods and need to remember the names for these methods, which is another lazy thing to do.

Another way, you can write "MySettings" singleton class with static constants and getter methods. For eg:


public class MySettings{
   public static final int SETTINGS_SHOUTER_ENABLED = 1;
   public static final int SETTINGS_CALL_SHOUTING_ENABLED = 2;
   public static final int SETTINGS_SMS_SHOUTING_ENABLED = 3; 
   .... 
 

   public Object getSettings(int settings){
      switch(settings){
         case SETTINGS_SHOUTER_ENABLED:
            return /* get value from preference */;
         case SETTINGS_CALL_SHOUTING_ENABLED:
            return /* get value from preference */;
         ... 
      }
   } 
} 

This way developer only has to remember only the return type for each settings (boolean, int, float, string ) ie: whether getSettings(SETTINGS_SHOUTER_ENABLED) returns Boolean or something else. But this is not optimized.

We need to optimize much more, because every time we are accessing preference, which is almost like reading a file, there is so much overhead. We can solve or avoid this overhead, by using  variable which will store the state. We will write data to this state variable when application starts/ when settings changes. Like the following
 
public class MySettings{
    public static final int SETTINGS_SHOUTER_ENABLED = 1;
    public static final int SETTINGS_CALL_SHOUTING_ENABLED = 2;
    public static final int SETTINGS_SMS_SHOUTING_ENABLED = 3;
    ....   
    private Object shouterEnabled, callEnabled, smsEnabled;
 
    public Object getSettings(int settings){
       switch(settings){
           case SETTINGS_SHOUTER_ENABLED:
             return shouterEnabled;
           ....
       } 
    }
 
    public void onApplicationStart(Context context){
        shouterEnabled = /* get value from preference */;
        callEnabled = /* get value from preference */;
        ... 
    }
 

    public void onSettingsChanged(int settings,Object value){
       switch(settings){
         case SETTINGS_SHOUTER_ENABLED:
             shouterEnabled = value; 
             break;
         ....
       } 
    }
} 

where onApplicationStart() method will be called once when application starts, and onSettingsChanged()  method will be called when a particular settings is changed. onSettingsChanged() method will be used within PreferenceActivity and called when preference is changed, using PreferenceChangeListener.

                           The above coding can be obtained exactly using ENUM class, which is more like a static class, where we don't have to set integer values to each constants and don't have to pass it to getSettings() / onSettingsChanged method , and we can access this ENUM anywhere in the app. Also we can add more useful methods like
  1. Check whether settings state will be changed before saving it into SharedPreferences.
  2. Check whether preference has changed when new value is applied.

public enum MySettings{
    SETTINGS_SHOUTER_ENABLED,
    SETTINGS_CALL_SHOUTING_ENABLED,
    .... 
    SETTINGS_SMS_SHOUTING_ENABLED;
 
    public Object value;
    public boolean changed = false;
 
    public boolean onSettingsChanged(Object newValue) {
           changed = newValue.equals(value) ? false : true;
           if(changed) value = newValue;
           return changed;
    }

    public boolean checkChanged(Object newValue) {
            return value.equals(newValue) ? true : false;
    }

    public static void onApplicationStart(Context context) {
            for (Preferences p : Preferences.values()) {
                p.changed = false;
            }
            
        shouterEnabled = /* get value from preference */;
        callEnabled = /* get value from preference */; 
        ... 
    } 
 
    public Object getSettings(){
       return value;  
    }


This ENUM coding is so simple, concise, easy to re-use, more understandable than any of the above of the other methods.For eg: Developer just need to call MySettings.SETTINGS_SHOUTER_ENABLED.value to check whether shouting is enabled or not. And methods like MySettings.SETTINGS_SHOUTER_ENABLED.onSettingsChanged().

And best part is, this ENUM MySettings can be accessed any where in the project, in a much more concised/optimized/faster way.


Try this out. And let me know if i am doing something wrong, that using static stuff is not good for memory efficiency, or something like that. Thanks, enjoy reading rest of the blogs :)

Comments