Showing posts with label technique. Show all posts
Showing posts with label technique. Show all posts

Monday, February 18, 2013

Android Constants: Preference Keys, Actions, Extras and more

The content of this post may seem ... well .. trivial at first, but I have tripped over these so many times that I decided to write it up - at least to keep me reminded of it, if not for any other reason!

If you have written anything more than a HelloWorld app in Android, chances are you have had to work with a plethora of program elements that are represented as Strings. Consider this sampling:

  • Keys for SharedPreferences are Strings
  • Keys for Bundles are Strings
  • Intent extras are Bundles, and hence, if you want to include any extras or retrieve them from Intents, you use their String keys to work with them. Ditto with Fragment arguments
  • Intent and IntentFilter actions (and categories) are Strings themselves
  • . . .

I used to deal with these the lazy way: Declare the keys as public static where they are first used (or where they "belong" logically) and refer to them from wherever they are needed in the code. Examples of the class that is the logical owner might be:

  • The class that broadcasts an Intent
  • The class that creates or sends a non-broadcast Intent (this might be an Activity or Service for example)
  • The class that creates a SharedPreference for editing

However, I quickly found out that often it is not possible to cleanly define these keys as belonging to a particular class. Further, since you might end up with a handful of extras, qualifying the class name becomes tedious - more so since it is likely that Activities or Services can have quite long names. How readable is this snippet?

if(AbstractBaseLiveModeActivity.ACTION_LIVE_UPDATE.equals(intent.getAction())){
    Bundle extras = intent.getExtras();
    if(extras.containsKey(AbstractBaseLiveModeActivity.EXTRA_LIVE_UPDATE_TIMESTAMP)){
        long timestamp = extras.getLong(AbstractBaseLiveModeActivity.EXTRA_LIVE_UPDATE_TIMESTAMP);
        // Do something with timestamp here
    }
}

Constants Almighty

One common solution to this problem is to put everything into one "God" object called Constants or whatever, and prefix the constant names with EXTRA_, ACTION_ or other such descriptive characters to keep them distinct.

public class Constants{
    private Constants(){}

    public static final String ACTION_LIVE_UPDATE = "com.myawesomeapp.action.LIVE_UPDATE";
    // ...

    public static final String EXTRA_LIVE_UPDATE_TIMESTAMP = "com.myawesomeapp.extra.LIVE_UPDATE_TIMESTAMP";
    // ...

}

Now, we've solved the readability problem since we just qualify the constant names with Constant. So, all's well, right?

Wrong!

The problem with this approach is as the number of extras, actions and preference keys increases, the Constants class quickly becomes unmanageable. Also, having to use the ACTION_ and EXTRA_ prefixes hinders usability with some IDE's. For example, with Eclipse, even if you know that you want EXTRA_LIVE_UPDATE_TIMESTAMP, you are forced to type the first six characters without which the code assist will not be able to filter only the extras.

Try using Eclipse to find a particular action or extra from the Intent class if you want to see a real-world example of what I mean.

Split it up into distinct constant files

Here's what I do to keep my code free of such stutter. I simply split up the "God" Constants class into several smaller, easier-to-manage constants classes. Like so:

public class Extras{
    private Extras(){}

    private static String createExtra(String suffix){
        return Constants.NAMESPACE_PREFIX + ".extra."+suffix; //NAMESPACE_PREFIX could be "com.myawesomeapp"
    }

    public static final String LIVE_UPDATE_TIMESTAMP = createExtra("LIVE_UPDATE_TIMESTAMP");
    public static final String LIVE_UPDATE_VALUE = createExtra("LIVE_UPDATE_VALUE");
    public static final String FRIEND_ID = createExtra("FRIEND_ID");
    // ...

}

public class Broadcasts{
    private Broadcasts(){}

    private static String createBroadcast(String suffix){
        return Constants.NAMESPACE_PREFIX + ".broadcast."+suffix; //NAMESPACE_PREFIX could be "com.myawesomeapp"
    }

    public static final String LIVE_UPDATE = createBroadcast("LIVE_UPDATE");
    public static final String FRIEND_OFFLINE = createBroadcast("FRIEND_OFFLINE");
    // ...
}

public class Actions{
    private Actions(){}

    private static String createAction(String suffix){
        return Constants.NAMESPACE_PREFIX + ".action."+suffix; //NAMESPACE_PREFIX could be "com.myawesomeapp"
    }

    public static final String JOIN_CHAT = createAction("JOIN_CHAT");
    // ...
}

You could create classes for Categories, Preference Keys and so on. Note that I differentiate between Broadcasts and Actions because although they are both Intents, they are logically very different. Now, this code snippet changes to:

if(Broadcasts.LIVE_UPDATE.equals(intent.getAction())){
    Bundle extras = intent.getExtras();
    if(extras.containsKey(Extras.LIVE_UPDATE_TIMESTAMP)){
        long timestamp = extras.getLong(Extras.LIVE_UPDATE_TIMESTAMP);
        // Do something with timestamp here
    }
}

Which code snippet would your rather see, especially six months from now when you have to fix a bug? Also note that we've made it far more easy to find the exact action or extra that we want using our IDEs.

Wait, what about constants in XMLs?

Glad you asked. In android, many of these constants are used not only in Java code, but also from XML files.

  • Preference keys can be referenced in preferences XML files via the <PreferenceScreen> element.
  • Intents can be declared in AndroidManifest.xml. This means, the Intent action and categories can be referenced from the manifest.
  • BroadcastReceivers can be declared in AndroidManifest.xml. The <intent-filter> action and categories are referenced here.
  • . . .

This presents a problem since we end up duplicating the constants here. We cannot use our Broadcasts.LIVE_UPDATE constant in XML, so we tend to repeat the constant value:

<intent-filter>
    <action android:name="com.myawesomeapp.broadcast.LIVE_UPDATE"/>
</intent-filter>

This is not good. Any change to any constant involves updating it at multiple places. What's more, these issues are not caught at compile time and can be hard to debug.

Using String resources to avoid duplication

One way to avoid constant literal duplication issue explained in the previous section is to use string resources. You are already using string resources for a variety of strings in your Android app. (Wait, you aren't? I strongly suggest you start doing so right now). All you need to do is add the constants as additional string resources.

To keep things clean, you could keep these constants in their own file under values/ folder - for example constants.xml. In there, you could add

<resources>

    <!-- Broadcast Actions -->
    <string name="broadcast_live_update">com.myawesomeapp.broadcast.LIVE_UPDATE</string>
    <string name="broadcast_friend_offline">com.myawesomeapp.broadcast.FRIEND_OFFLINE</string>

    <!-- Intent Extras -->
    <string name="extra_live_update_timestamp">com.myawesomeapp.extra.LIVE_UPDATE_TIMESTAMP</string>
    <string name="extra_live_update_value">com.myawesomeapp.extra.LIVE_UPDATE_VALUE</string>
    <string name="extra_friend_id">com.myawesomeapp.extra.FRIEND_ID</string>

    <!-- Preference Keys -->
    <string name="pref_key_update_interval">UPDATE_INTERVAL</string>
    <string name="pref_key_theme">THEME</string>

</resources>

Then, you could access these values from XML as follows:

<intent-filter>
    <action android:name="@string/broadcast_live_update"/>
</intent-filter>

<Preference 
    android:key="@string/pref_key_update_interval"
    ... />

UPDATE Jun 2015: This does not work. The android:name attribute does not take a string resource. It MUST be a string itself. The approach works for Preferences though

And so on. In Java code, you'd access these as:

if(getString(R.string.broadcast_live_update).equals(intent.getAction())){
    // ...
}

mSharedPref.getLong(getString(R.string.pref_key_update_interval));

Unfortunately, this solution has all the disadvantages I mentioned in an earlier section.

Your constants.xml will quickly become a monolithic clutter. This can be addressed by creating a separate XML file for each type of constant - like broadcasts.xml, pref_keys.xml etc. Even if you do that, you will still be accessing all the resources using @string/blah and R.string.blah.

Also, IDE content assist is still a problem. Your resource names will need to be prefixed with action_ or broadcast_ or pref_key_ etc and finding the key you need could be frustrating.

A workable strategy

Here's a strategy I follow to decide how I should declare these constants:

  • For preference keys, prefer string resources. This is because you are most likely to be building your Settings screens with XML anyway.
  • For all other key constants, prefer split constant files.
  • Only if you need to use these from XML, declare them as string resources.

Monday, March 19, 2012

Override and Debug

What would we ever do without a debugger eh? In the Java world, it’s JPDA. Integrated with your favourite IDE, it makes debugging your source code a breeze. Just set a breakpoint someplace in your code, and then step through.

But wait. What happens when you step into code that’s outside your project? What if you step into a method that’s from a library, or from the core platform itself? You end up seeing something illegible: some bytecode, or just a “Source not found” message (if you are using Eclipse that is). Go ahead: try it out:
  • Write a bare minimum “Hello World!” application.
  • Put a breakpoint at the line where you print the message to System.out.
  • Now debug your application and step into the System.out.println method.
What do you see?

 

Solutions?

The best solution, of course, is to attach the source code, if available. Unfortunately, most of the time the source code is not available, and even if it is, “attaching” it is not the easiest thing to do. If you have worked with Android, you know what I mean.

What’s the second-best solution? Use the override-debug technique. Although this technique does have its limitations (as we will discuss later in this article), it does turn out to be useful in a large number of use-cases. The technique is as follows:
  • Choose the library method that you wish to debug. Note that you might not care about the method that you call directly from your code. Instead, you might be interested in a method, say, four levels deep in the call stack.
  • Extend the class which contains this method, and override it.
  • Simply call the super method in the overriden implementation.
  • Finally, while debugging, put a breakpoint in the brand new one-liner method that you just implemented.

Congrats! You’ve just managed to see what’s happening four levels deep in the call hierarchy; without even having the source code.

 

Show me some Code!:

Much of that did not make sense, did it? Probably because it was all text and no code? Fear not, for the code example is here!

I use an (admittedly contrived) example to demonstrate this technique. I have a Laundry class and a doLaundry() method within it which takes a bunch of clothes as arguments. This method
  • First invokes the WashingMachine object to wash and rinse the clothes.
  • Then it calls upon a Dryer to spin them.
  • Finally, it hangs out the clothes to dry on a Clothesline.
All of this is within a library for which you don't have the source.The objective is to debug what is happening when the control reaches the Clothesline.hang() method.

public class LaundryRunner {
  private static Laundry laundry = new Laundry();

  public static void main(String[] args) {

    /*
     * Initialize the clothes for laundry
     */
    List clothes = new ArrayList(3);
    clothes.add(new ClothingItem("shirt", "white", "stained"));
    clothes.add(new ClothingItem("jeans", "blue", "dirty"));
    clothes.add(new ClothingItem("t-shirt", "red", "dirty"));

    /*
     * Objective: To try to debug whats going on inside.
     * For example, what happens by the time the clothes reach the clothesline?
     */
    laundry.doLaundry(clothes);
   }
}

You could put a breakpoint at the doLaundry() invocation, but since you don't have the source attached, you draw a blank.

The solution is to extend the Clothesline class as follows:

public class CustomClothesline extends Clothesline {

   @Override
   public void hang(List clothes) {
    System.out.println("Debugging in Custom Clothesline");
    System.out.println("Status of clothes given to me:");
    for(ClothingItem item: clothes){
     System.out.println(item.getStatus());
    }
    super.hang(clothes);
  }
}

And then, instruct the Laundry class to use your custom Clothesline instead of the default one.

laundry.setLine(new CustomClothesline());
laundry.doLaundry(clothes);

This entire project and source code is available here. I have included the dependent project as a JAR library, but it is also available as a separate project in case you want to look at it.

Ok that was just an example code. To see the override-debug technique in the real world, see this project on Google Project Hosting (pay special attention to CustomDrawable.java ). Here, I would ordinarily have used a BitmapDrawable object, but I wanted to know what happens when the onBoundsChanged() method is called. I achieved this by extending BitmapDrawable and overriding the method I was interested in.

 

Limitations:

The override debug technique has the following constraints:
  • The class that you want to debug must be extensible. This means it must be declared public and not final.
  • The method which you want to override must be, well, overridable. This means it must have a visibility of at least protected, and of course it should not be declared final.

 

An aside:

You’re probably thinking that the above two limitations render this technique more or less useless. After all, as a best practice, developers of libraries and frameworks are unlikely to allow overriding of their classes and methods unless they are explicitly designed to do so (for example life-cycle methods).

It turns out in practice this is not much of a constraint. Frameworks like Android allow you to extend and override almost anything. The following is a stackoverflow discussion on this topic.