Android コレクション、直列化、永続化
●コレクション
Collectionインターフェースを実装したコレクションクラスのことで、
データセットの管理に重要。
●Bundle
コレクション実装の1つ。Bundleはキー(String)、バリューの組を保持する。
バリューには、Androidに最適化されたオブジェクト郡を使うことができる。
IntentのExtrasは、内部でBundelオブジェクトを管理している。
□マッピングの追加・取得
・put~():追加
・get~():取得
※~は型が入る
ex) Intentの例(呼び出し元のActivity)
public void send(View v, int value) {
Intent intent = new Intent(this, ResultActivity.class);
Bundle bundle = new Bundle();
bundle.putString("data","value"); ←★put(型はString)
intent.putExtra("bundlePrams", parcelableData);
startActivity(intent);
}
ex) Intentの例(呼び出し先のActivity)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Bundle extras = getIntent().getExtras();←★get(型はString)
Bundle bundlePrams = extras.getParcelable("bundlePrams");
Toast.makeText(this, "data:" + bundlePrams.getString("data"), Toast.LENGTH_LONG).show();
}
●SparseArray
key(int)としたHashMapのようなもの。keyをint型に固定し、HashMapより高速に動作する。
Sparseという名の通り、keyに使用するint型が連続でなく(Sparse)とも使用できる。
AndroidのR.idで管理されるidはint型であるため、SparseArrayを用いることができるケースが
多くなります。(Android Listが警告を出してくれる。)
SparseArrayはvalueの型によってはvalueも最適化したSparseBooleanArray、SparseIntArrayを
使うことができる。
大規模である場合は、LongSparseArrayを使用することもできる。
ex)
SparseArray<String> array = new SparseArray<String>();
array.add(R.id.TextView1, "hoge");
array.add(R.id.TextView2, "fuga");
array.add(R.id.TextView3, "foo");
array.add(R.id.TextView4, "bar");
●直列化
Androidでは2種類の直列化フレームワークを利用可能。
Javaで提供されるものと、Androidで提供されるものである。
□Serializable(シリアライズ)
- Javaフレームワーク
- オブジェクトの保存と復旧の方法を決めたもの
- 永続化
利用するI/Oの仕組み→データ化・データ復旧を保証する
- ObjectInputStream
- ObjectOutputStream
マーカーインターフェースである:メソッドの宣言のないインターフェース
クラスのフィールドがもつデータを扱う。
staticやtransientな変数、メソッドは直列化しない。
ex)
public class MyObject implements Serializable {
public static final long serialVersionUID = -4324129709521L;
private String mName;
}
【Notice】クラスのフィールドの互換性を管理する必要がある
※参考
http://d.hatena.ne.jp/asakichy/20120216/1329343931
http://d.hatena.ne.jp/Kazuhira/20130929/1380443535
→柔軟性、品質、安全性を熟考する必要がある
□Parcel、Parcelable
- Androidフレームワーク
- 永続化ではない
- Serializableより軽量
- プロセス間通信でハイパフォーマンスを得るために設計されたインターフェイス
- IntentやBundleはParcelableを実装している
Parcelableのインターフェースに書き込み処理と復元処理を定義することにより、
メッセージングで型を限定されずにオブジェクトを渡すことが可能。
- Parcelableの実装クラスの中にParcelableではないクラスを含めることが可能
- 異なるアプリケーションへのメッセージングでも同一オブジェクトの復元を保証
Parcelableの実装手順
- Parcelable#writeToParcel()メソッド内でParcelに対してデータを書き込む
- CREATORという定数を定義し、Parcelable.Creator<T>を実装
- Parcelable.Creator#createFromParcel()でParcelからオブジェクトを生成する処理を実装
【Notive】Parcelから読み込む順序は、Parcelable#writeToParcel()で書き込んだ順序と同じにする
ex) ParcelableをIntentで受け渡しする
・受け渡すParcelableの実装
public class MyParcelable implements Parcelable { ←★Parcelbleをimplements
public static final Parcelable.Creator<MyParcelable> CREATOR = new Parcelable.Creator<MyParcelable>() { ←★CREATORの実装
public MyParcelable createFromParcel(Parcel in) { ←★Parcelからオブジェクトを生成する処理
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private int mData;
public MyParcelable() {}
private MyParcelable(Parcel in) {
mData = in.readInt();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel out, int flags) { ←★writeToParcelでParcelにデータを書き込む
out.writeInt(mData);
}
public void setData(int data) {
mData = data;
}
public int getData(){
return mData;
}
}
・Parcelの送信元(呼び出し元Activity)
public void send(View v, int value) {
Intent intent = new Intent(this, ResultActivity.class);
MyParcelable parcelableData = new MyParcelable();
parcelableData.setData(value);
intent.putExtra("parcelableData", parcelableData);
startActivity(intent);
}
・Parcelの受信先(呼び出し先Activity)
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Bundle extras = getIntent().getExtras();
MyParcelable parcelData = extras.getParcelable("parcelableData");
Toast.makeText(this, "data:" + parcelData.getData(), Toast.LENGTH_LONG).show();
}
□JSONObjectとJSONArray
- JSONはオブジェクトのシリアライズ・デシリアライズ形式の1つ
- JSONを用いたフレームワーク
- JSONRPCなど主にネットワーウ経由でのデータの転送に利用する
JSONObject:JSONにおけるオブジェクトに対応
JSONArray:JSONにおける配列に対応
JSONObjectからの値の取得方法は2種類
1. JSONObject#get~():~は型
- JSON文字列上の型と異なる型を取得するメソッドを用いた場合、適宜型が変換される(安全ではない)
- 存在しないkeyの場合、JSONExceptionがスローされる
2. JSONObject#opt~():~は型
- 存在しないkeyを指定した場合、フォールバックの値が使用される。
【Notive】値がnullかどうか判定するには、JavaのNULLを使用できない。
JSONObject#isNull() を使うこと。
□Gsonフレームワーク
- JSON文字列からJavaのオブジェクトへマッピングするフレームワーク
【Notice】端末依存に注意
http://alpha.mixi.co.jp/entry/2013/11572/
●永続化
Androidで用意された永続化手法
□ここで扱うもの
- Shared Preferences
- Internal Storage
- External Storage
□別で扱うもの
- SQLite Databases(データベース)
- Network Connection(ネットワーク通信)
●SharedPreferences
- Android標準で用意されている永続化の方法の1つ
- keyとvalueの組み合わせでプリミティブ型やString型のデータを永続化する
- ファイルの読み書き、状態管理・監視を請け負うので、自前でI/Oを準備するより簡単
□ファイルの実体
- ファイルはアプリのもつ内部ストレージに保存される
- アプリのアンインストール時は、すべてのデータが削除される
- アプリが再インストールされてもなおデータを永続化したいユースケースには適用できない
- →そのような場合は適宜サーバにデータを保存するようにする
【Notive】保存場所が違うことに起因する問題
http://alpha.mixi.co.jp/entry/2013/11572/
→ Android-Device-Compatibilityライブラリの使用の検討
https://github.com/mixi-inc/Android-Device-Compatibility
□ファイルへのアクセス権
- モード:SharedPreferencesのファイル作成時に設定するアクセス権限。アプリの領域内のアクセス権をファイル単位でコントロールする。
- 他のアプリからのアクセスを許可することは推奨されない。
- →アプリ間連携のための仕組みを使用する。
- 複数のプロセスをもつアプリケーションで同じファイルを参照する場合、モード:MODE_MULTI_PROCESSが存在するが、API Level 11以降となる。
□SharedPreferencesの使用
・インスタンスの取得
Context#getSharedPreferences(String, int)
:第1引数(ファイルの名前)
:第2引数(モード、デフォルトはMODE_PRIVATE)
□データの保存
SharedPreferences.Editorを介して行う- SharedPreferences.Editorオブジェクトの取得:SharedPreferences#edit()
- 値の設定:SharedPreferences.Editor#putString(String,String)
- 保存:SharedPreferences.Editor#commit()、SharedPreferences.Editor#apply()
【Notice】
SharedPreferences.Editor#apply()は保存の完了を待たない、かつAPI Level9以降となる。
SharedPreferencesを扱っていて、保存の成功・失敗を意識しなくていい場合は、
非同期処理の中でSharedPreferences.Editor#commit()を呼ぶ。
- 値の削除:SharedPreferences.Editor#remove(String)
- すべての値の削除:SharedPreferences.Editor#clear()
【Notive】
値の削除も、SharedPreferences.Editor#commit()またはapply()を呼び出すまで完了しない。
ex)
// データの保存
public boolean savePerson(String name, int age) {
SharedPreferences sp = getSharedPreferences("person", MODE_PRIVATE);
Editor editor = sp.edit();
editor.putString("name", name);
editor.putInt("age", age);
return editor.commit();
}
□データの取得
- SharedPreferences#get~():~は型
【Notive】
保存時の型と取得時の型が一致していること
ex)
// データの取得
private String mName;
private int mAge;
public void readPerson() {
SharedPreferences sp = getContext().getSharedPreferences("person", MODE_PRIVATE);
mName = sp.getString("name", "no name"); ←★String型を取得(第2引数はフォールバック)
mAge = sp.getInt("age", 0);
}
□データ変更の監視
保存されている値に変更が合った場合、UIに反映する処理などに使用する。- SharedPreferences.OnSharedPreferenceChangeListener:データ変更監視のリスナーオブジェクト
- SharedPreferences#registerOnSharedPreferenceChangeListenerでリスナーオブジェクトを登録
【Notive】
このリスナーオブジェクトのライフサイクル管理は実装で行う必要がある。
すなわち、SharedPreferences#unregisterOnSharedPreferenceChangeListener()をContextのライフサイクルの終わりに行う。
ex)
public class MainActivity extends Activity implements OnSharedPreferenceChangeListener {
private SharedPreferences mSharedPreferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSharedPreferences = getSharedPreferences("sample", MODE_PRIVATE);
mSharedPreferences.registerOnSharedPreferenceChangeListener(this);
}
protected void onDestroy(){
mSharedPreferences.unregisterOnSharedPreferenceChangeListener(this); ←★リスナーの解除
super.onDestroy();
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
String value = sharedPreferences.getString(key, null);
if (value != null) {
TextView tv = (TextView) findViewById(R.id.PreferencesValue);
tv.setText(value);
}
}
}
●Internal Storage
- 内部ストレージのこと。
- モードによってアクセス制御が可能
- 容量の制限があることが多いので大量データの保存はできない
□Contextが提供する内部ストレージ利用のインターフェイス
・ファイルを保存
- FileOutputStreamの取得:Context#openFileOutput()
- バイト列をストリームに格納:FileOutputStream#write()
- ストリームを閉じる:FileOutputStream#close()
【Notice】
FileOutputStream#close()処理はfinallyで行うこと
ex)
String FILENAME = "hello_file";
String string = "hello world!";
FileOutputStream fos = null;
try {
openFileOutput(FILENAME, MODE_PRIVATE);
fos.write(string.getBytes());
} catch (IOException e) {
// 例外処理
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
}
}
・ファイルの内容を取得
- FileInputStreamを取得:Context#openFileInput()
- ファイル内容を読み出し:FileInputStream#read()
- ストリームを閉じる:FileIntputStream#close() 【Notice】finallyで呼び出すこと
●CacheDirectory
- 一時ファイルとしてキャッシュする特別なディレクトリ
- 内蔵ディレクトリにキャッシュ用ディレクトリが作成される
●External Storage
- 外部ストレージ
- SDカード
- 端末によっては、内部とは別の内蔵メモリが存在する。(ハイエンド端末に多い。Galaxy、Nexus)
- アクセス制御できない
- 全てのアプリケーションからアクセスが可能
- ルートディレクトリの取得:getExternalFilesDir()
- 取得するディレクトリのtype
DIRECTORY_MUSIC
DIRECTORY_PODCASTS
DIRECTORY_RINGTONES
DIRECTORY_ALARMS
DIRECTORY_NOTIFICATIONS
DIRECTORY_PICTURES
DIRECTORY_MOVIES
0 件のコメント:
コメントを投稿