Edited at

Key-Valueデータをresourceで定義する

More than 3 years have passed since last update.

Androidアプリの初期データとして、Key-Value型で値を持ちたいというのはよくある事だと思います。

単純な文字列や配列であればContext#getStringResources#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);
}
};
}

特に問題はなさそうに見えますが、コードとデータはなるべくなら分離したい所です。

調べた所、以下の様にすれば簡単に実装する事ができます。



  1. 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>


  1. 以下のクラスを実装し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程には効率的に処理できないという点です。

軽量なデータセットであれば上記実装を検討しても良さそうな気がします。


参考

偉そうに書きましたが以下の記事の和訳となります。

Lightweight key-value pairs for Android bundled reso...