SharedPreferencesモドキを作ろう

 XmlSerializer なんかをイジって、xml 関係がだいたい飲み込めた。
 しかし、データ・フォーマットを考えるのはめんどくさい。読み込み部が専用になるのも、なんかアレよね。
 読み込みは遅いが、やっぱ融通の効くSharedPreferences は便利だ。
 しかし、SharedPreferences は外部ファイルを読み込めない…。
 なら作ってしまえ、というワケ。
 どうせint とString しか使う予定もないし。
 そんなに面倒でもないだろう。

package com.migimaki.android;

import android.content.Context;

import java.io.FileInputStream;
import android.util.Xml;
import org.xmlpull.v1.XmlPullParser;

import android.util.Log;


/*
 * Preferences Reader クラス
 * 
 */
public class PrefReader {

	private Context context;
	public String fPath = "/";

//====================
//初期化
//====================
	/**
	 * コンストラクタ
	 */
	public PrefReader( Context c, String fPath ){
		context = c;
		this.fPath = fPath;
		//--
	}


//====================
// read method
//====================

	////////////
	/// Read Int
	public int getInt( String key, int default_i ){

		int result = default_i;
		//--

		try{
			FileInputStream is = new FileInputStream( fPath );
			XmlPullParser xmlPullParser = Xml.newPullParser();
			xmlPullParser.setInput( is, "UTF-8" );

			
			int i = xmlPullParser.getEventType();
			do{
				if( i == XmlPullParser.START_TAG 
						&& xmlPullParser.getName().equals( "int" )
							&& xmlPullParser.getAttributeValue( null, "name" ).equals( key )
					){

						result = Integer.parseInt( xmlPullParser.getAttributeValue( null, "value" ) );
						break;
				}
				i = xmlPullParser.next();
			}while ( i != XmlPullParser.END_DOCUMENT );

			is.close();

		}catch( Exception ex ){
			Log.d( "Err", "PrefReader : getInt" );
		}
		//--
	//	Log.d( "TEST", "PrefReader " +key +":" +result );
		return result;
	}
	///==
	
	////////////
	/// Read String
	public String getString( String key, String default_s ){

		String result = default_s;
		//--

		try{
			FileInputStream is = new FileInputStream( fPath );
			XmlPullParser xmlPullParser = Xml.newPullParser();
			xmlPullParser.setInput( is, "UTF-8" );

			
			int i = xmlPullParser.getEventType();
			do{				
				if( i == XmlPullParser.START_TAG 
						&& xmlPullParser.getName().equals( "string" )
							&& xmlPullParser.getAttributeValue( null, "name" ).equals( key )
					){

						result = xmlPullParser.nextText();
						break;
				}
				i = xmlPullParser.next();
			}while ( i != XmlPullParser.END_DOCUMENT );

			is.close();
			
		}catch( Exception ex ){
			Log.d( "Err", "PrefReader : getString" );
		}
		//--
	//	Log.d( "TEST", "PrefReader " +key +":" +result );
		return result;
	}
	///==
	
}

 できた。int とString だけだけど。(w

 各method でFileInputStream を設定しているのは、そうしないと読み込み位置が保持されてしまうため。

 SharedPreferences には、int とString の他に、boolean, float などなどがある。
 それらはおそらく、int とほぼ変わりがないだろうと思う。
 必要になったら、作るとしよう。

Posted in File, XML. Tags: , . SharedPreferencesモドキを作ろう はコメントを受け付けていません »

プレリファレンスを置き換えてみるテスト

 セーブデータを扱うには、SharedPreferences がお手軽でお気軽。
 だけど、格納場所がdataディレクトリと、固定なのが難点。
 というのも。
 いま作成中のアプリでは、dataディレクトリに作らないから。

 作成中のアプリは、ノベル・リーダー。
 複数の作品データを切り替えることになる。
 そのため、各作品ごとに、セーブデータが必要になり、その場所は作品データのある場所に置こうと考えている。

	1. 作品ごとにデータを作る。
	2. 場所は作品データと同じ。( not dataディレクトリ )

 なので、SharedPreferences の仕様は、ちと困るのである。

置き換えてみよう

 格納場所が固定なら、置き換えてしまえばいいんじゃない?
 しかし、そんなことが可能なのだろうか…?

 ということで、このテストをやってみた。
 単純にこういう実験である。

	1. pref.xml を記録
	2. pref.xml をSDへコピー
	3. pref.xml のデータ変更
	4. SDからpref.xml をコピー
	5. pref.xml を読み込む
 5.で元データの内容が読み込めれば、置き換え成功!

 さて、その結果はというと…失敗である。
 読み込んだ内容は、3で書き換えたものであった。
 なにが原因だろう?

仮説 a. 読み込みmethod に不具合がある。
 これはない。
 まず、1の直後に記録内容のチェックに使用し、正常動作している。
 それと単純に失敗している場合、デフォルト値が返ってくるハズなのだが、それとは異なり、3で記録した値である。

仮説 b. ファイルのコピーに失敗している。
 まぁ、コレであろう。
 そう思って、ターミナルから/data/data へ潜ってみる。
 pref.xml は存在し、more で中身を表示したところ、それは1の時の内容だった。
 はて?
 これはどういうことだろう?
 読み込みmethod に原因はなく、ファイルもちゃんと置き換わってる。
 なのに、値は元に戻らない…。
 摩訶不思議である。
 Android、おまえはドコから、ナニを読んでいるのだ?

消してみよう

 pref.xml を読んでいないことはまず確実である。
 では、その値はドコから?

 そこで4の時点で、pref.xml を消してみることにした。
 読み込めないので、デフォルト値が返ってくるハズだ。
 しかし、結果は変わらず、3の時の内容だ。
 まったくもって面妖ナリ。
 ファイルがないのに、その値をひっぱってきていることになる。
 まるで、
「あなたの後ろに幽霊がいますよ?
 私には見えるんです…ククク…」

 なんていわれた気分。
 う~む…。
 ファイルの消し方がマズイのだろうか?

 ならばと、Editerから値をクリアすることにしてみた。
 Editerとはオブジェクトのひとつで、値を記録するときに使用する。
 先程はファイルごとを削除する方法。
 今回は内容を消してしまう方法。
 さて、結果はというと…成功である。
 いや、読み込みには失敗、値はデフォルト値を返してきた。
 つまり想定した動作だ。

 では、今度は書き込まず、いきなり読んでみよう。
 つまり再起動直後にはどうなるか?
 結果は再起動前に、コピーした内容が正常に読み込めた。
 なるほど。
 なんとなくわかりかけてきた…。

ということは、どういうことなのだろう?

 ここから先は確かめるつもりもないので、推論の域である。

推論 a. Androidが整合性をチェックしている。
「メモリー上のなにかとファイルが異なる場合、メモリー上のデータを信頼する」
 セキュリティからするとありそうな話しではある。
 しかし大げさなので、ちと眉唾な気も。

推論 b. なんらかのlockがかかっている。
 ファイルは削除や置き換えが可能なので、メモリー上のlockだろう。
 手続き上のミスか、またはキャッシュの類かもしれない。
 aよりはありえそうな話しだね。

 ともかくも。
 確実な結論はただひとつ。
 SharedPreferences を使う計画は見直し。
 手間をかけて読み込むしかなさそう…くすん。

by the way…

 ついでに、色々と見て回っての発見を書いておこう。

 SharedPreferences は/data/data/%Package Name%/shared_prefs/ に格納される。
 でも、ファイル・マネージャーの類で見ても、/data/data はないのである…。
 そんじゃと、ターミナルから入ってsuしてみたら…
 あった!!
 なるほど。 /data/data のオーナーはsystemで、ユーザーには不可視になってる。
 で、/shared_prefs のオーナーは、app_29。
 おそらく、作ったアプリなんだろうね。
 中のxmlも、同じくapp_29 だった。
 ちなみに、/shared_prefs と同列に /lib なんてのがある。
 オーナーは system。中身はカラ。
 他のアプリと共有するようなclassが入るんだろうかね。

Posted in テスト. Tags: , . プレリファレンスを置き換えてみるテスト はコメントを受け付けていません »

SharedPreferences ~セーブとロード

 ゲームなんかのプレイ・データを保存するには、SharedPreferences がラクでいいね。
 扱いも簡単だし、書き込み/読み込み順番とか気にしなくてもいい。

データの書き込み

 下記、例では指定したファイル(xmlName)に、3つのデータを書き込んでいる。

	// write
	public void write_xml( String xmlName ,int i, String key, String key2 ) {
		// MODE_WORLD_READABLE:他のアプリから読み取り可能
		// MODE_WORLD_WRITEABLE:他のアプリから書込み可能
		// MODE_PRIVATE:当アプリのみ使用可能

		SharedPreferences pref =
			getSharedPreferences( xmlName, MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE );
		Editor edit = pref.edit();
		
		edit.putInt( "score", i );
		edit.putString( "key", key );
		edit.putString( "key2", key2 );
		
		edit.commit();
	}

 まず、getSharedPreferences で、書き込むファイルを取得する。
 引数のxmlName には、xmlのファイル名を指定する。
 ただし、拡張子はつけない。
 pref.xml だとしたら、”pref” となる。

 次に、Editor を取得する。
 書き込みは、このEditor を介して行われる。

 書き込み内容の設定は、putInt などを使う。

	edit.putInt( "score", i );
		第一引数 : データ名
		第二引数 : データの値

 putIntの他にも、putString, putBoolean, putLong, putFloat が使える。

 設定をしたら、commit() でファイルに書き込む。

 ちなみに、commit() と同じ役割のapply() がある。
 違いは結果を返すか、返さないか。
 commit() だと boolean を返してくれるので、その値を調べれば成功したかがわかる。

データの読み込み

 下記、例では指定したファイル(xmlName)から、3つのデータを読み込んでいる。

	// Read
	public void read_xml( String xmlName ) {
		
		SharedPreferences pref =
			getSharedPreferences( xmlName, MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE );
		
		int i = pref.getInt( "score", 0 );
		String str = pref.getString( "key", "" );
		String str2 = pref.getString( "key2", "" );

	Log.d( "TEST", "read | score :" +i +" key:"+str +" key2:"+str2 );
	}

 手順は、書き込みの時とほぼ同じ。
 method の第二引数は、デフォルト値となる。
 なにかの原因で読み込めなかった場合、この値が返ってくる。

データの削除

 すべてのデータを削除するには、下記のようになる。

	// clear
	public void delete_data( String xmlName ) {
		SharedPreferences pref =
			getSharedPreferences( xmlName, MODE_WORLD_READABLE | MODE_WORLD_WRITEABLE );
		Editor editor = pref.edit();
		editor.clear();	// all clear
		editor.commit();
		//--	
	}

 editor を取得して、clear()。その後、commit() を行う。
 commit() をしないと、反映されないので注意。
 データを指定して消す場合は、remove を使う。

	remove( String key );

 なお、全削除には、xmlファイルごと消してしまう方法もある。
 しかしその場合、すぐには反映されず、アプリの再起動が必要となるようだ。

Posted in File. Tags: , . SharedPreferences ~セーブとロード はコメントを受け付けていません »