Providing a config files for your Android app

2015/09/15

Ever needed to embed a configuration file in your Android app? What’s the best practice? As far as I know, there isn’t one. Here’s a nice solution. What’d you’d like is to be able to define your configuration in the same format as an Android resource file, like this,

<string name="url">http://www.foo.com</string>

You can put your resource in strings.xml (or any other file in res/values), but then you are stuck with completions. Do you want R.string.url to be a valid string resource in your app? No, it’s not a localizable string and having it respond to code completion isn’t the right thing. Moreover, if you are providing a library, you will expose your configuration values to developers that import your library.

You can put an XML resource file in res/xml, but Android isn’t going to parse it. Here’s a simple helper class to parse a a res/xml resource as an Android resource file,

public class XmlResourceParser {
  protected final Context context;

  private final Map<String, String> strings = new HashMap<String, String>();
  private final Map<String, Integer> integers = new HashMap<String, Integer>();
  private final Map<String, Boolean> booleans = new HashMap<String, Boolean>();

  public XmlResourceParser(Context context) {
    this.context = context;
  }

  public void parse(int id) throws XmlPullParserException, IOException {
    XmlPullParser xpp = context.getResources().getXml(id);
    int eventType = xpp.getEventType();

    while (eventType != XmlPullParser.END_DOCUMENT) {
      if (eventType == XmlPullParser.START_DOCUMENT) {
      } else if (eventType == XmlPullParser.START_TAG) {
        String tag = xpp.getName();
        if ("string".equals(tag)) {
          strings.put(xpp.getAttributeValue(null, "name"), xpp.nextText());
        } else if ("integer".equals(tag)) {
          integers.put(xpp.getAttributeValue(null, "name"), Integer.valueOf(xpp.nextText()));
        } else if ("boolean".equals(tag)) {
          booleans.put(xpp.getAttributeValue(null, "name"), Boolean.valueOf(xpp.nextText()));
        }
      } else if (eventType == XmlPullParser.END_TAG) {
      } else if (eventType == XmlPullParser.TEXT) {
      }
      eventType = xpp.next();
    }
  }

  public String getString(String key, String def) {
    if (strings.containsKey(key)) {
      return strings.get(key);
    }
    return def;
  }

  public int getInt(String key, int def) {
    if (integers.containsKey(key)) {
      return integers.get(key);
    }
    return def;
  }

  public boolean getBoolean(String key, boolean def) {
    if (booleans.containsKey(key)) {
      return booleans.get(key);
    }
    return def;
  }
}

Obviously this only handles strings, ints, and bools. The rest is left for an exercise for the reader. You use it like this,

XmlResourceParser xrp = new XmlResourceParser().parse(R.xml.myresources);
String url = xrp.getString("url");
Advertisements