Androidアプリの初期データとして、Key-Value型で値を持ちたいというのはよくある事だと思います。
単純な文字列や配列であればContext#getString
やResources#getStringArray
などで取得すれば良い話ですが、Key-Value形式は中々良いやり方がなく以下の様にシングルトンなMapを作ってメモリに展開している実装をよく見ます。
public final class DatatMap {
private DataMap() {
}
public static final Map<String, Integer> ICON_MAP = new HashMap<String, Integer>() {
{
put("100", R.drawable.icon_100);
put("200", R.drawable.icon_200);
put("300", R.drawable.icon_300);
put("400", R.drawable.icon_400);
}
};
}
特に問題はなさそうに見えますが、コードとデータはなるべくなら分離したい所です。
調べた所、以下の様にすれば簡単に実装する事ができます。
-
res/raw/
に以下のフォーマットでデータを作成します。
<?xml version="1.0" encoding="utf-8"?>
<extras xmlns:android="http://schemas.android.com/apk/res/android">
<extra
android:name="mega"
android:value="1000000" />
<extra
android:name="kilo"
android:value="1000" />
</extras>
- 以下のクラスを実装し
ResourcesAdditions.getResourcesExtras(getResources(), R.raw.extras)
といった形で使用します。
public final class ResourcesAdditions {
public static final String TAG_EXTRAS = "extras";
private ResourcesAdditions() {
// No instance
}
public static Bundle getResourcesExtras(Resources res, int resId) throws Resources.NotFoundException {
return getResourcesExtras(res, TAG_EXTRAS, resId);
}
public static Bundle getResourcesExtras(Resources res, String rootTag,
int resId) throws Resources.NotFoundException {
XmlResourceParser parser = res.getXml(resId);
int type;
try {
while ((type = parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) {
// Empty loop
}
if (type != XmlPullParser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}
if (!parser.getName().equals(rootTag)) {
throw new XmlPullParserException("Unknown start tag. Should be '" + rootTag + "'");
}
final Bundle extras = new Bundle();
res.parseBundleExtras(parser, extras);
return extras;
} catch (Exception e) {
final Resources.NotFoundException nfe = new Resources.NotFoundException();
nfe.initCause(e);
throw nfe;
}
}
}
使い方は以下の様になります。
Bundle bundle = ResourcesAdditions.getResourcesExtras(mResources, R.xml.extras);
assertEquals(4, bundle.size());
assertEquals(10, bundle.getInt("deca"));
assertEquals(100, bundle.getInt("hecto"));
assertEquals(1000, bundle.getInt("kilo"));
assertEquals(1000000, bundle.getInt("mega"));
この実装のメリットは読み込みの実装がAndroidに依存した形になっているので軽量な事、デメリットは大きなデータになるとGsonのdeserialization程には効率的に処理できないという点です。
軽量なデータセットであれば上記実装を検討しても良さそうな気がします。
参考
偉そうに書きましたが以下の記事の和訳となります。