お久しぶりです。高畑です。
最初の挨拶を「こんにちは」か「お久しぶりです」のどちらにしようか迷いました。
今回はAndoridアプリ開発には欠かせない機能の1つである、SharedPreferencesに色々なオブジェクトを保存する方法について書こうと思います。
SharedPreferencesとは
SharedPreferencesはアプリ内でデータを保存する方法のひとつで
xml形式で保存されます。
主な利用目的は、アプリの設定値の保存やタスクを終了しても残しておきたい情報を保存するために使います。
手軽に実装できるので、利用する場面は多いと思います。
iPhoneアプリで言うところの「NSUserDefaults」に近いものがありますが
SharedPreferencesは保存できる型はかなり限られており
現在以下の型のみ保存できます。
1 2 3 4 5 6 |
boolean String int long float Set<String>//API level 11以上 |
例えば以下のようなコードでSharedPreferencesに値を入れて保存して
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public class MyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SharedPreferences pref = getSharedPreferences("pref",MODE_PRIVATE); SharedPreferences.Editor editor = pref.edit(); editor.putBoolean("boolean", true); editor.putString("string", "hoge"); editor.putInt("int", 100); editor.putLong("long", 100L); editor.putFloat("float", 100F); HashSet<String> stringHashSet = new HashSet<String>(); stringHashSet.add("set1"); stringHashSet.add("set2"); editor.putStringSet("stringSet", stringHashSet); editor.commit(); } } |
保存したファイルの中身を見ると以下のようなxmlになっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <boolean name="boolean" value="true" /> <set name="stringSet"> <string>set2</string> <string>set1</string> </set> <long name="long" value="100" /> <float name="float" value="100.0" /> <int name="int" value="100" /> <string name="string">hoge</string> </map> |
emulatorで実行している場合にSharedPreferencesの中身を取り出したい時は
DDMSでdata/data/<アプリのパッケージ名>/shared_prefs/<ファイル名>
にファイルが保存されているのでそれを取り出します。
実機の場合はadbコマンドを使って中を見ることができます
1 2 3 4 |
$ adb shell $ run-as <パッケージ名> $ cd shared_prefs $ cat <ファイル名> |
色々なオブジェクトを保存する
SharedPreferencesは非常に便利ですが、上記の通り保存できるオブジェクトの型に制限があります。
そこで役に立つのがGsonです。
GsonはオブジェクトをJSON文字列に変換・復元できるライブラリです。
SharedPreferencesに対応した型以外のオブジェクトを保存したい場合は
保存したいオブジェクトをJSON文字列にして、putStringで保存する事ができます。
復元するときには、getStringで取得したJSON文字列をGsonを用いてオブジェクトに復元する事ができます。
例えば以下のようなUserクラスがあったとします
1 2 3 4 5 6 7 8 9 10 11 |
public class User { private String name; private int age; private Date birthday; private ArrayList<String> stringArrayList; private HashMap<Integer,String> hashMap; //以下setter,getter記述省略 } |
UserのプロパティにDateやArrayListなど、SharedPreferencesに対応していない型も入れてみます。
このクラスに値をセットしてGsonでJSON文字列に変換してSharedPreferencesに保存します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
public class MyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SharedPreferences pref = getSharedPreferences("pref",MODE_PRIVATE); User user = new User(); user.setName("名前"); user.setBirthday(new Date()); user.setAge(27); ArrayList<String> strings = new ArrayList<String>(); strings.add("list1"); strings.add("list2"); strings.add("list3"); user.setStringArrayList(strings); HashMap<Integer,String> hashMap = new HashMap<Integer, String>(); hashMap.put(1,"map1"); hashMap.put(2,"map2"); hashMap.put(3,"map3"); user.setHashMap(hashMap); //GsonでUserをJSON文字列に変換して保存する Gson gson = new Gson(); gson.toJson(user); pref.edit().putString("user",gson.toJson(user)).commit(); } } |
SharedPreferenceの中身は以下のようになっています。
1 2 3 4 |
<xml version='1.0' encoding='utf-8' standalone='yes' ?></p> <map> <string name="user">{"stringArrayList":["list1","list2","list3"],"birthday":"Jan 28, 2014 4:44:12 PM","hashMap":{"1":"map1","2":"map2","3":"map3"},"name":"名前","age":27}</string> </map> |
「”」がエスケープされて見にくいですが、エスケープを戻すと以下のような感じです
1 2 3 4 5 |
<map> <string name="user"> {"stringArrayList":["list1","list2","list3"],"birthday":"Jan 28, 2014 4:33:47 PM","hashMap":{"1":"map1","2":"map2","3":"map3"},"name":"名前","age":27} </string> </map> |
JSON形式で保存されているのがわかります。
復元するときは、以下のように行います。
1 2 3 4 5 6 7 8 9 10 11 |
public class MyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SharedPreferences pref = getSharedPreferences("pref",MODE_PRIVATE); Gson gson = new Gson(); //Userオブジェクト復元 User user = gson.fromJson(pref.getString("user",""),User.class); } } |
こんな感じに非常に簡単に復元できます。
ただ、BitmapをGsonで文字列に変換するのは利用に耐えないほど遅く、ANRが発生する可能性が高いので使えないです。
どうしても画像を保存したい場合はBase64での文字列変換をお勧めします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public class MyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); SharedPreferences pref = getSharedPreferences("pref",MODE_PRIVATE); //BitmapをBase64で文字列にしてSharedPreferenceに書き込む Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream); String bitmapString = Base64.encodeToString(outputStream.toByteArray(), Base64.DEFAULT); pref.edit().putString("bitmap",bitmapString).commit(); //SharedPreferenceに保存した文字列からBitmap復元 byte[] bytes = Base64.decode(pref.getString("bitmap","").getBytes(),Base64.DEFAULT); ImageView image = (ImageView)findViewById(R.id.imageView); image.setImageBitmap( BitmapFactory.decodeByteArray(bytes, 0, bytes.length) ); } } |
Base64への変換は高速なので、うまい具合にGsonと使い分けると良いと思います。
しかし、この方法でもあまり大きい画像を変換すると、少しアプリの動きが詰まります。
小さめのプロフィール画像などをキャッシュしておく程度の使い方に止めておきましょう。
Gsonと組み合わせれば、TwitterやFacebookのAPIで取得したユーザー情報ぐらいなら保存しておくことが可能です。
最後に
SharedPreferencesにあまり大きなデータを保存すると
アプリケーションのサイズがどんどん膨らむのでいかがなものかと思いますが
こういう使い方もできるということで何かの参考になれば嬉しいです。
iOSに浮気を始めたAndroidエンジニア? Androidはほとんど書いてない…