ZipFile ~画像サイズが大きいと読み込み失敗

 先日のZipから画像を読み込む method に不具合があった。
 どうもBitmapFactory の時点で、読み込みミスをしているようす…。

 調べてみると、同様の症状に行き着いた。
http://groups.google.com/group/android-group-japan/browse_thread/thread/197c76b8fb3a8cc6?fwc=1
 どうやら、画像のデータサイズが大きいと、InputStream が読み込みミスをするらしい。
 回避するには、一度どこかに解凍してから、読み込むしかなさそう。

※.キャッシュファイルの保存
https://sites.google.com/a/techdoctranslator.com/jp/android/guide/data-storage

どのくらいだと大きい?

 素朴な疑問。
 サイズが大きいって、どのくらいだと大きいの? (?_?)

 読み込もうとしたのは、800×600のJPG。
 先日の試作アプリでは問題なかったものなんだ。
 じゃ、なにがちがうのかといえば、アプリ。
 不具合が出たのは、作ってるノベルアプリに組み込んでからなんだ。
 だから最初はノベルアプリを疑ったんだけど、そうじゃなさそうなのは前記のとおり。
 ではナニが制約になってるのかというと、たぶんヒープなんじゃないかと思う。

 試作アプリはZIP関係のClass しかないような状態で、ヒープに余裕がある。
 一方、ノベルアプリは色々と組み込んでいるから、ヒープに余裕があまりない。

 どのくらいだと”大きすぎる”かは、一概にいえないんじゃないかな?
 そういうワケで。
 直接メモリーに展開するのは、避けた方がよさそう。
 ”小さい”と思っていても、ヒープからすると「すごく大きいです…」になるかもしれないから。(w

by the way…

 原因を調べている途中で、BitmapFactory.Options なるものを見かけたよ。
 これを使うと、解像度や色数、サイズなんかを指定して、読み込めるらしい。
 GIFやPNG、サムネールなんかにはよさげなオプションだね。

※.コチラのページが詳しいです。
http://d.hatena.ne.jp/hyoromo/20101001/1285943744

Posted in File, 読み込み. Tags: , , , . ZipFile ~画像サイズが大きいと読み込み失敗 はコメントを受け付けていません。 »

ZipFile と ZipEntry ~Androidでzipを解凍する

 いわゆるアドベンチャー・ゲームみたいに、複数のデータファイルを扱う場合、それらをひとつのファイルにまとめることがほとんどだよね。
 その理由は、機密性が第一なのかな?
 でも配布を考えた場合、ひとつにまとまっていた方が都合いいんだ。
 まず、コピーが早い。
 ファイルの取りこぼしがない。
 それと一概にはいえないけど、通信パケットを節約できる。

 で、ひとつにまとめるには、どうしたらいいか?
 色々と方法はあるけど、オイラはzipを使うことにしている。
 なぜかといえば、まとめるツールは作らなくていいし、取り出すルーチンも簡単ですむから。

 というワケで、zipからデータを取り出すmethod を作ってみたよ。

ZipFile と ZipEntry

 zip を扱うのに、使うclass は以下の2つ。

●ZipFile
http://developer.android.com/reference/java/util/zip/ZipFile.html

●ZipEntry
http://developer.android.com/reference/java/util/zip/ZipEntry.html

 ZipFile は、そのままZIP を抽象化したもの。
 ZipEntry は、ZIPの中のファイルを抽象化したもの。

 method を眺めると、ZipEntry と Fileクラス はなんか似ている。
 たぶん、「ZipFile はHD、ZipEntry はファイル」と見立ててるんだろうね。
 取り出すときもInputStream を使うから、ファイル操作と手順はそう変わらなそう。

 ちなみに、ZipInputStream というclass もある。
 今回は使わなかったけど、役割はZipFile と同じみたいだ。
 おそらく、ネットからZIPを取得した時、メモリー上にZIPがある時、なんかに使うんだろね。

ファイル一覧を取得する

 まず、中のファイル一覧を取得してみた。
 下記method は、ZIPのパスを指定すると、中のファイル一覧をString配列として返すよ。

	public int err = 0;	// エラーがあったか? // -1 : エラー有り | other : エラーなし

	public String[] getList( String zipPath ){
		err = 0;
		String[] result = null;
		//--

		try{
			ZipFile zipFile = new ZipFile( zipPath );
			
			List<String> list = new ArrayList<String>();
			Enumeration<? extends ZipEntry>	enu = zipFile.entries();
			while( enu.hasMoreElements() ){
				ZipEntry entry = enu.nextElement();
				//--
				list.add( entry.getName() );
			}
			zipFile.close();
			
			result = (String[])list.toArray(new String[0]);
		}catch( Exception ex ){
			err = -1;
			Log.d( "Err", "// ZipIO : getList err... //" );
		}

		//--
		return result;
	}

 まず、ZIPのパスから、ZipFile オブジェクトを生成する。

 そして、entries() で、Enumeration を取得する。
 このEnumeration は、中のファイル、──つまりZipEntry の塊みたいなモンだと思っていい。
 Enumeration のnextElement() を使うと、順繰りにZipEntry を取り出せるんだ。

 で、取り出したZipEntry にgetName() をして、ファイル名を取得している。
 取得したファイル名は、いったんList オブジェクトに格納するよ。
 List オブジェクトは配列と似たようなものだけど、要素数を増やすことができるんだ。
 お陰でファイルの総数がわからなくても、配列を作ることができる。
 そして、ファイル名の格納が終わったら、List をtoArray()でString配列に変換するんだ。

 以上で、ファイル一覧を取得できた。
 応用として、getName() をgetSize() にすると、容量一覧が取得できるね。
 ※.getSize() は、long を返します。

 注意点は、ZipFile のclose。
 最後にclose してやらないと、メモリーリークする可能性があるから注意。

文字バケ

 一覧を取得できたので、画面に表示してみた。

文字バケ

 …と。なんかバケてるね。(^_^;
 Windowsで作ったZIPなので、ファイル名がShift-JISなのかもしれない。
 それじゃ、Macで作ったZIPならバケない…?

文字バケ

 …見事にバケてるよ。(w

 これはたぶん、Javaレベルの問題。
 1バイト文字専用で、2バイトやUTF-8に対応していないんだね。
 バケ方のちがいからすると、そんな感じ。

 2バイト文字に対応するには、取得したファイル名をデコードしてやらないといけないんだね。
 まぁ、解凍には支障ないんで、今回はパスしておくよ。

ファイルを解凍してみた

 次に、ファイルを指定して、解凍してみた。

 下記method で、指定したファイルを解凍するよ。
 手順は、ファイルのコピーとそう変わらないね。

 引数は、
  ZIPのパスを指定
  解凍するファイル名
  解凍先(Path付ファイル名)

	public int err = 0;	// エラーがあったか? // -1 : エラー有り | other : エラーなし

	public void meltFile( String zipPath, String file, String outFile ){
		err = 0;
		try{
			ZipFile zipFile = new ZipFile( zipPath );
			ZipEntry entry = zipFile.getEntry( file );
			InputStream is = zipFile.getInputStream( entry );
			
			OutputStream os = new FileOutputStream( outFile );

			byte[] buffer = new byte[ 1024*4 ];	// 1024*4 = DEFAULT BUFFER SIZE
			int r = 0;
			while( -1 != ( r = is.read(buffer)) ){
				os.write( buffer, 0, r );
			}

			os.close();
			is.close();
			zipFile.close();
		}catch( Exception ex ){
			err = -1;
			Log.d( "Err", "// ZipIO : meltFile err... //" );
		}

		//--
	}

 ディレクトリの中にファイルがある場合、解凍するファイル名をPath付にしてやる。
 こんな感じ。

	dir/char.gif

 ただし、2バイトの場合、↓ではダメ。

	新しいフォルダ/char.gif

※.UTF-8 のZIPなら解凍できるかも…?

 たぶん、「1バイト化け」した状態で指定してやれば解凍できるんじゃないかな?
 もしくはファイル名以外の方法で、ZipEntry を取得してやればいいと思う。

 ちなみに上記サンプルでは、解凍先にディレクトリは作られない。
 必要があるなら、別途つけくわえてね。

画像データを取得してみた

 データとして取り出すにはどうしたらいいんだろ?
 ここにひとつ問題があるんだ。

 一覧取得でちょっと触れたけど、ファイルサイズの単位は、long なんだ。
 これではbyte配列自体が作れない。
 int サイズに限定すればできるけど、そういう制限はかけたくない。

 となると、InputStream から直接、目的のデータにすることになりそう。
 というワケで。
 画像データ(Bitmap)として取り出すmethod を作ってみた。

	public int err = 0;	// エラーがあったか? // -1 : エラー有り | other : エラーなし

	public Bitmap meltBitmap( String zipPath, String file ){
		err = 0;
		Bitmap result = null;
		//--

		try{
			ZipFile zipFile = new ZipFile( zipPath );
			ZipEntry entry = zipFile.getEntry( file );
			InputStream is = zipFile.getInputStream( entry );
			result = BitmapFactory.decodeStream( is );

			is.close();
			zipFile.close();
		}catch( Exception ex ){
			err = -1;
			Log.d( "Err", "// ZipIO : meltBitmap err... //" );
		}

		//--
		return result;
	}

 BitmapFactory で、InputStream からBitmap にしている。
 この方法は、ネットから画像データを取得するのと同じ方法。

 Drawable で取り出す時には、

	Drawable.createFromStream

 を使えばいいよ。

 取得した画像データをImageView で表示してみた。
 問題なし。(^_^)
※.画像サイズが大きいと、失敗することがあるらしい。
 詳しいくは…

「ZipFile ~画像サイズが大きいと読み込み失敗」

解凍表示

by the way…

 個人的に困ったのは、RandomAccessFile が使えないことだね。
 ヘッダー位置の取得方法がないから、部分的に読み込むことができない。
 できないワケじゃないけど、イロイロと面倒なんだな、これが。(^_^;
 一度テンポラリーに、ファイルとして解凍するのがいいのかな…。

Posted in File, 読み込み. Tags: , , , , , , , . ZipFile と ZipEntry ~Androidでzipを解凍する はコメントを受け付けていません。 »

体を成した、ところで…

 作ってるノベルアプリ。
 ファイル選択、動作設定の後、再生…
 順次テキストを読み込み、表示させているので、ノベルアプリとしては体を成した、といったところ。
 あとは絵と音を出せば、だいたいできあがり…といった感じだね。

 で、ここまできて全体を眺めてみると、なんともイビツな印象。プログラム的にね。
 xmlとJavaソースの連携、Layout への理解を深めると、いかにAndroid風でない構成かがわかる。組むのに苦労するワケだ。
 習得しながら、構成ヒネって、押し込んだんだから、仕方ないやね。(^_^;
 ……でも、作り直したい…。(-_-;

 まぁ、作り直しは我慢して。
 絵を出すところまでやってみよう。
 音は…VirtualBox だと出ないんだなぁ…。実端末入手するまで保留かね。

 で。必要となる、zip周りを作成開始なのである。

Posted in 雑記. 体を成した、ところで… はコメントを受け付けていません。 »

TextView のredraw

 いろいろとイジくっていたら、TextView が正常表示されなくなった。
 部分的に欠けた表示になるんだ。
 再描画させればいいことはわかるんだけど、その方法がわからない…。
 で、getText してsetText したら、うまいこといったよ。

	setText( (String) getText() );

TextView の再帰ループ

 いま作ってるプログラムでは、TextView を二枚使ってる。
 ひとつはメッセージ表示。
 もうひとつはデバッグ・モニター。

 それでメッセージ側に、タッチを即すアイコンを出しているんだけど…。
 これまた、ひょんなことから表示されなくなってしまったんだ。
 原因を調べていたら、デバッグ・モニターも表示されなくなってたのに気づいた。
 こっちは単純な表示フラグの問題だったんで、直したところ、なんとアイコンも表示されるようになったんだ。
 どうやら、デバッグ・モニターの再描画を、メッセージ側も拾っていたらしい…。
 Σ そんなん、アリですか?! (笑

 でも、デバッグ・モニター側でのsetText って、onDraw の中でしかやってないんだよね。
 ふしぎ…
 って、まてまて。
 ということは、↓ってことだよ。

 onDraw → setText →onDraw →setText →…

 どう見ても、再帰ルーチン。
 つまり、スレッドなしで、安全な描画ループができるってことだよ。
 これはまた、妙な技を見つけてしまった…。

by the way…

 TextView なのに定期描画されてるの、なんか、おかしいと思ってたんだ。(笑

Posted in 裏技, 雑記. Tags: . TextView のredraw はコメントを受け付けていません。 »

TextViewはスクロールできません…

 表示するテキストが表示枠からはみ出したら、スクロールバーを出そう。
 そう思って調べてみると…
 TextViewはスクロールできません…
 どうやらスクロールさせるためには、ScrollView を使わなきゃならないらしい。

 ScrollViewは、ViewGroup の一種らしい。
 おそらく一種の窓みたいなもので、傘下のView の表示位置を変えて、スクロールさせるんだろうね。
 昔、そんなのを作ったよ…。

 つまりやるには、メッセージ表示のTextView を、ScrollView の傘下に配置しなきゃいけない。
 構造変更しなきゃならないよ… orz

 クリックでのメッセージ送りもあるし、今回は見送るか。

Posted in View, 雑記. Tags: , . TextViewはスクロールできません… はコメントを受け付けていません。 »

RandomAccessFile ~Shif-JISで読み込む

 RandomAccessFile では、文字コードの指定はできない。
 Shift-JISも扱いたいので、別項のソースに手を加えてみたよ。

	public long head_pos = 0;	//現在のseekヘッド位置

	public String readline_RA( String path, long readPos, String code ) {
//	Log.d("TEST", "readline_RA : "+path );

		String result = "";
		//--

		try{
			File file = new File( path );
			RandomAccessFile ra = new RandomAccessFile( file, "r" );
			ra.seek( readPos );

			String str = ra.readLine();
			if ( str!= null ){
				result = str;
			//	}else{
				//	result = "";
				//	Log.d("Err", "// readline_RA : read is null //" );
			}
			head_pos = ra.getFilePointer();

			//
			if ( code != "UTF-8" ){
				// 改行コード判定
				//	CR : 0x0D : \r
				//	LF : 0x0A : \n
				//	UNIX = LF : Mac = CR : DOS = CR+LF

				byte[] buffer = new byte[ 2 ];	// 行末2バイトを読み込む
				ra.seek( head_pos -2 );
				ra.read(buffer);
				long RT = 1;
				if ( ( buffer[0] == 0x0D )&&( buffer[1] == 0x0A ) ){	//DOS
					RT = 2;
				}
				//--

				ra.seek( readPos );
				buffer = new byte[ (int)( head_pos -RT -readPos ) ];	// 読み込む量
				int r = ra.read(buffer);
				if ( -1 != r ){
					result = new String( buffer, code );
				}
			}
			//--

			ra.close();
		}catch( Exception e ){
			Log.d("Err", "// readline_RA : read ERR //" );
		}
		
		//--
		return result;
	}

仕組み

 手を加えた部分のは、23行~46行。

	1. readLine で読み込んだ範囲を、byte 配列で読み込み直す。
	2. byte 配列を文字コードを指定して、String に変換。

 readLine のデータを直接変換するのがシンプルなんだけど…。
 そのデータは、すでにUTF-8のためのbyte を差し挟まれちゃってる。
 そんなbyte 配列をプレーンな状態にするより、もう一度読み込んだ方が、結果的にシンプルなんだよね。
 直接変換するようなものがあるのかもしれないけど。

 だもんで、readLine は「一行の範囲を調べる」だけになってる。(^_^;

読み込み範囲

 最初の位置からgetFilePointer までを読み込むと、改行コードがbyte 配列に含まれてしまう。
 これをそのままTextView へ表示すると、不明文字のトウフが表示されちゃう。
 それは都合がわるいので、改行コードを調べて、読み込み範囲を補正する必要がある。(30~36行目)
 調べた値は long RT に格納し、読み込み範囲の補正に使っている(39行目)

文字コード指定

 指定した文字コードで変換するのは、難しくない。
 byte から、文字コードを指定して、String を作るだけ。(43行目)

	result = new String( buffer, code );

by the way…

 引数 code は、String で文字コードを指定することにしている。
 シフトJISなら、”Shift_JIS”。
 試してないけど、EUC-JPにも対応してるハズ。

Posted in File, 読み込み. Tags: , , . RandomAccessFile ~Shif-JISで読み込む はコメントを受け付けていません。 »

RandomAccessFile ~ファイルの読み込み

 ファイルを扱うclass は色々あるね。
 今回はRandomAccessFile を使ってみたよ。

RandomAccessFile の利点

 RandomAccessFile を使うと、読み込み位置なんかを指定できるんだ。
 これはゲームの再開なんかに、とても都合がいい。

RandomAccessFile での読み込み

 下記サンプルでは、テキスト・ファイルを一行読み込みしている。

	public long head_pos = 0;	//現在のseekヘッド位置

	public String readline_RA( String path, long readPos, String code ) {
//	Log.d("TEST", "readline_RA : "+path );

		String result = "";
		//--

		try{
			File file = new File( path );
			RandomAccessFile ra = new RandomAccessFile( file, "r" );
			ra.seek( readPos );

			String str = ra.readLine();
			if ( str!= null ){
				result = str;
			//	}else{
				//	result = "";
				//	Log.d("Err", "// readline_RA : read is null //" );
			}
			head_pos = ra.getFilePointer();

			ra.close();
		}catch( Exception e ){
			Log.d("Err", "// readline_RA : read ERR //" );
		}
		
		//--
		return result;
	}

モード設定

 RandomAccessFile は書き込み、読み込みで、別class になっていない。
 そのため、オブジェクト生成時に、モードを設定する必要がある。
 下記では、読み込み専用として生成している。

	new RandomAccessFile( file, "r" );

位置設定

 書き込み位置、読み込み位置の設定は、seek method を使う。
 値はlong で指定する。

	seek( readPos );

位置の取得

 書き込み位置、読み込み位置の取得は、getFilePointer method を使う。
 値はlong 。

	getFilePointer();

by the way…

 RandomAccessFile では、文字コードの指定はできない。
 そのためテキストファイルは、デフォルト・コード(UTF-8)で扱われてしまう。
 Shift-JISを扱うには、別途自前で直す必要がある。
 コレに関しては、別項・

Shif-JISで読み込む
にて。

Posted in File, 読み込み. Tags: , . RandomAccessFile ~ファイルの読み込み はコメントを受け付けていません。 »

リソースからのファイルコピー

 エミュレータにテスト・ファイルを送りたい。
 WebからDownLoadする、コンソールからwgetする…?
 いろいろやり方はあるけど、リソースからコピーできると便利。
 「デモファイルのインストール」なんて機能にもなるしね。

 やり方はシンプルなコピーとそう変わりがない。
 リソースのpathを取得するには、Resources class を使うのだが。
 このResources からは、InputStream しか生成できない。
 そのため、FileChannel は使えないんだ。

 下記サンプルでは、SDカードのルートにファイルをコピーしている。

	public void cpResTo_SD( Context context, int res_id, String fName ) {
		
		// Copy先の取得
		File cpFile = new File( Environment.getExternalStorageDirectory().getPath()+"/" +fName );
//	Log.d( "TEST", "copy to :" +cpFile.getPath() );
	
		// exec copy
		try{
			
			Resources res = context.getResources();
			
			InputStream is = res.openRawResource( res_id );
			OutputStream os = new FileOutputStream( cpFile );

			byte[] buffer = new byte[ 1024*4 ];	// 1024*4 = DEFAULT BUFFER SIZE
			int r = 0;
			while( -1 != ( r = is.read(buffer)) ){
				os.write( buffer, 0, r );
			}

			is.close();
			os.close();
			
		}catch( Exception ex ){
			Log.d( "TEST", "/res -> copy error..." );
		}
		//--
	}

 DEFAULT BUFFER SIZE が 1024*4 となっているのは、ファイルの最小サイズが4kだから。
 これより大きくても、小さくても問題ないけど、4kにしておくのが一番、効率がいい。

 注意点としては、リソースの場所があげられる。
 たとえばテキスト・ファイルなんかを、/values に格納するとeclipse でエラーとなる。
 /raw なんかを作り、そこに格納しておく必要がある。

Posted in File, コピー. Tags: , , . リソースからのファイルコピー はコメントを受け付けていません。 »

スレッドから別オブジェクトを触ると異常終了?! 解決編

 先日の「スレッドからオブジェクトをイジると異常終了する」件。
 2つの方法で解決した。

 おそらくは邪道な方法だと思うので、参考ぐらいにとどめた方がいいと思う。(^_^;

 前提となる、アプリの構造は以下のような感じだ。

	Activity
	  └GameRun (スレッド)
	     └TextView

※.この他にも、TextView と同列のView があるのだが、ここでは省略。

【動作】
 GameRun がスレッドとして動作し、表示メッセージをTextView へ送る。

【問題】
 GameRunスレッドから、setText や class変数の変更など、TextViewの操作を行うと異常終了する。

 解決するには、Handler を使用するしかないようだ。
 ただそれには不都合があり、今回は、別の方法を探ってみた。

リスナーを使う

 Handler ときて、最初に連想したのがこの方法だ。
 リスナーも通知先をハンドラという。
 ならば、リスナー経由で操作を行えば、根本の原因を回避できるのではないか?
 というワケ。

	GameRun
	 ↓ 通知 ( _Listener.setPadding( padding[] ); )
	TextView
	  method が実行され、変数に値が格納される。
	   setPadding( padding[] ){
	      mesPadding = padding;
	   }

 ただしこの方法は、必ずmethod を介さなければならない。
 上記の例でいえば、スレッドを使わない場合、次のようにシンプルだ。

	TextView.mesPadding = padding;

 ただの代入でも、method を介さなければならないのは、ちょいとシャク。
 ただそれだけの話ではあるんだけどね。

 この方法はことのほか、すんなり問題を解決した。
 ソースもすっきりシンプル。
 値を返すようなものにも使用できる。

リスナー と Handler を使う

 前記方法で問題は解決したように見えたが、別の箇所で再び同じ問題が起きた。
 そこではsetTextやLayoutView のremove を使っており、前記方法では解決されなかったのだ。
 おそらく、描画しているものに対して、直接操作を行おうとしたからだろう。

 解決策としてまず思い浮かんだのが、タイマーである。
 タイマーはキューを受けると、時間をおいて処理を実行する仕掛け。
 View側であらかじめ、setTextやremoveするようなタイマーを作り、スレッド側はキューを送る。
 処理の実行はView側なので、根本原因は回避できるハズだ。
 ……って、待て待て。
 それって、Handler そのものなんじゃね? (^_^;

 そこでView のmethod にHandler を仕掛け、↓のように改良してみた。
 結果は成功である。

View側 method

	private Handler mHandler = new Handler();
	private String mesStr = "";

	public void setMes( String mes ){
		mesStr = mes;
		mHandler.post( new Runnable() {
			public void run() {
				setText( mesStr );
			}
		});
		
	}

スレッド側 method実行

	_MesView.setMes( "うふふ……" );

 View側で、引数 mes を、class変数 mesStr に一旦代入しているのは、そうしないとrun内で認識されないから。(シンタックス・エラーになる)
 というのも、new Runnable() で暗黙的にThread を生成しているからだ。
 つまりrun内は、別class または別method の領域となってしまうのだね。
 それはわかるが、イマイチ好きになれない書き方…。
 まぁ、是非もナシやね。

 これで問題は解決し、もうひとつの問題が発生した。
 先のsetText をappend にした場合である。

	public void appendMes( String mes ){
		mesStr = mes;
		mHandler.post( new Runnable() {
			public void run() {
				append( mesStr );
			}
		});
	}

 append は現在表示しているText に文字列を追加するmethodである。
 たとえば、こんな動作をする。

	[ソース]
	setText( "うふふ……" );
	append( "えっち♪" );

	[表示動作]
	うふふ……
	 ↓append
	うふふ……えっち♪

 ソースからすると、上記のような動作になるハズなのだが。
 実際にやってみると…こうなってしまう。

	うふふ……
	 ↓append
	えっち♪えっち♪

 これはclass変数を介しているために起きてしまう現象だ。
 普段、意識しないが、Javaでの変数は参照が基本。
 内部的にsetText されたのは、class変数のメモリー位置で、class変数の内容が変われば、当然、変わってしまう…。
 おそらくは、スレッドを介しているのも関係があるんだろうね。

 そこでappend は使わず、自前で追加するように変更した。
 これで意図どおりに実行されるようになった。

	public void appendMes( String mes ){
		mesStr = mesStr+mes;
		mHandler.post( new Runnable() {
			public void run() {
				setText( mesStr );
			}
		});
	}

 以上の方法には、リスナーのみの時とは、異なる注意点がある。

1. 即応性がない。
 実行タイミングは、Handler(Looper) 任せなので、いつ実行されるか不明だ。
 付随して、返り値を求めるようなmethod には適応できない。

2. classローカルでも、Handler を介することになる。
 たとえば、例にあげたTextView自身がsetMesを実行すると、Handler を介した実行になる。
 なんだか、自分の尻を他人に拭いてもらうようで、まどろっこしい。(笑
 また、即応性の面からすると、潜在的なバグとなる可能性もある。
 必要なら、ローカル用とHandler用、別々のmethod を作った方がいいだろうね。

3. class変数で引数を保持する。
 先の失敗例のように、Handler が実行する前に、値が変わってしまわないようにしなければならない。

by the way

 setTextは、スレッドからの実行で、ちゃんと動作している場合と、異常終了する場合が見られた。
 これはsetTextだけの話ではない。
 おそらく動作する例は、その時はまだ画面に表示されていないか、UIがひとつだけの状態だったのが原因ではないかと思う。
 一度だけの実行で、不具合ナシと、安心してはいけないね。

補足

 Handler の使い方は、まだよく把握していない。
 見よう見まねの試行錯誤で、「一応、動作している」にすぎない。
 もっとうまく、正しい方法もあると思う。
 ちゃんとした解説を見て、試して、よく理解する必要があるね。(^_^;

 しかし…。
 なんだかJavaというより、Cocoaって感じの構造になったな。(w

Posted in Listener, 雑記. Tags: , , , . スレッドから別オブジェクトを触ると異常終了?! 解決編 はコメントを受け付けていません。 »

スレッドから別オブジェクトを触ると異常終了?!

 作成中のアプリに、SelectMenuを組み込んでみたところ、異常終了するようになってしまった。
 どうも、スレッドから別オブジェクトのView(UI系)に触ってはいけない決まりらしい。

 色々と調べてみたところ、その理由はスレッド間の同期にあるようだ。
 簡単にいうと、「個々に動いているスレッドで、勝手にイジくりまわされちゃかなわん」。
 まぁ、その理屈はわかるけどね。
 でも、コッチで作った変数に触っただけで、落ちる軟弱構造はどうよ?!

 それはそれとして。
 スレッドからの操作を行うための方法はあって、それがHandler らしい。
 Handler がスレッドからの通知を受け取り、溜まった通知を順番にLooper が実行していく…。
 そんな構造のようだね。
 なるほどねぇ。
 でも、ルーピーはイヤなネーミングだなぁ。オイラ、麻生支持者だし。(w

 対処方はわかったんで、試しにやってみたんだけど…。
 やってみると、問題が色々とでてくる。
 まず、変数が使えない。
 Handler はclass の中にclass を書くような記述のため、method 内で作った変数は参照できないみたいだ。(シンタックス・エラーになる)
 しかも、うまく動かない。
 なんか、Activity ともからんでいるようで、今の構造だとちと無理なようだ。
 どうもオイラの組み方は、とことん、Android と相性がわるいらしい。(w

 しかしこの、”class の中にclass がある” 書き方ってどうにかならんかね?
 ソースが見た目、すっきりしないし。
 iアプリやってたせいか、class をボロボロ作るのはどうもねぇ…。
 容量ケチりたがる貧乏性なだけだが。(笑

 まぁ、グチクヂいっててもはじまらない。
 Handler はスレッドを扱うゲームからすると、洗礼のようなもののようだし。
 どうにか対処を考えよう。

 こんな感じで対処してみたよ。

Posted in 雑記. Tags: , . スレッドから別オブジェクトを触ると異常終了?! はコメントを受け付けていません。 »