« 2011年2月 | トップページ | 2011年4月 »

2011年3月

2011年3月29日 (火)

中華Padふたつめがやってきた

来たには来たんですけどね、

率直にいって、あいつら最低です。

まず、傷だらけです。後ろのパネルには無数の引っかき傷があり、かなり鋭利なものでつけたと思われる深い傷もたくさんあります。中古というよりジャンクの体裁です。(もちろん、中古を買ったわけではありません)

そして、動きません。とりあえずACアダプタをつなぐとLEDが赤く点き、しばらくすると緑に変わるので、模造品ではなく動くことを意図して製造したらしいということは汲み取れるのですが、電源スイッチをいれてもうんともすんともいいません。

まぁ、修理あがりを(実際はロクに直ってないのですが)送りつけたのでしょうが、こちらからすれば、金はらったらゴミ送りつけてきやがった。というのが率直な感想。

とりえあず、返品交渉中。

2011年3月24日 (木)

ACTION_SENDの結果

Androidでは他のアプリに処理をお願いしたい場合にIntentという機構を使います。

メールを送ったりする場合にはACTION_SENDという種類のIntentを発行することになります。(実際はTwitterのクライアントなどもACTION_SENDのIntentFilterを持っているのですが)

今回のお話しは、じゃぁ、実際ACTION_SENDを発行するとどうなるの?というお話しです。

DeveloperサイトにはIntentとExtraの項にEXTRA_EMAILという項目があって、私が読む範囲では発行時にはメール送信先を指定できて、送信後に(ユーザが実際に指定した)アドレスを返してくれるように読めます。

じゃあ、Intentからこれを読み出して、次回メール送るときに渡せば親切じゃん。という発想がでてきますよね?やってみました。

      private OnClickListener textListener = new OnClickListener(){

            public void onClick(View v) {
                  String[] email = {"foo@example.com"};
                  Intent req = new Intent();
                  req.setAction(Intent.ACTION_SEND);
                  req.setType("text/plain");
                  req.putExtra(Intent.EXTRA_EMAIL,email);
                  req.putExtra(Intent.EXTRA_SUBJECT, title());
                  req.putExtra(Intent.EXTRA_TEXT, mSendMessage);
                  startActivityForResult(Intent.createChooser(req, getString(R.string.intent_chooser_title)),PLAIN_SEND);
            }
      };
コードとしてはこんな感じです。これ、実際発行するとどうなるのかというと、ほとんどのMailクライアントはメール送信後にActivity.RESULT_CANCELを返してきます。受ける側はというと、
      protected void onActivityResult(int req, int res ,Intent intent){
            super.onActivityResult(req, res, intent);
            
            Log.d(TAG,"onActivityResult: res = "+Integer.toString(res));
            if (res == Activity.RESULT_OK){
                  String classname = intent.getClass().getName();
                  Bundle extra = intent.getExtras();
                  String[] email = extra.getStringArray(Intent.EXTRA_EMAIL);
                  String[] cc    = extra.getStringArray(Intent.EXTRA_CC);
                  String[] bcc   = extra.getStringArray(Intent.EXTRA_BCC);
                  Log.d(TAG, "email ="+email[0]);
                  switch (req) {
                  case PLAIN_SEND:
                        break;
                  case XHTML_SEND:
                  }
            }
      }
こんな感じのコードで結果を受け取るわけでうすが、そもそも、最初のifのところを通りません。そして、その条件判定を外してExtraが付いているかを見ると、そもそもIntentがnullか、Extraが付いていてもその中がnullかのどちらかです。
すくなくともXperiaにインストールしている標準Mail、Gmail、SPモードメ-ルのどれもがRESULT_CANCELを返してきました。(もちろん、メールの送信は成功していますし、EvernoteクライアントはちゃんとRESULT_OKを返したので、chooserなりの中でResultが書き換えられているということはないはずです)。まぁ、1.6のエミュレータのMailクライアントは送信後に呼び出し元に戻りさえしないので、進歩しているといえばそうなのかもしれませんが。
ということで、小さな親切機能はボツになりそうです。

2011年3月21日 (月)

燃料切れ

ここのところ、家に居る日が続いています。

なので、飲食関係の消費が大きいです。家にいると夕方過ぎて車を運転する予定がないと飲み始めるわけですが、ここのところ、朝から予定というか燃料不足で動かすことができなくて、その分人間に燃料入れたりしています。

というわけで、酒が切れたので最後のガソリンを使って隣町のいつもの酒屋さんへいきました。

やってるか心配だったのですが、店内にお酒(とくに日本酒)の香りが漂ってはいましたが、割れた壜はすでに片付けた後で、棚にビンを横倒しにして並べて営業していました。

今回の獲物は

  • スペインのワイン(名前は忘れたけど、普通のテーブルワインです)
  • レミーマルタン(また?)
  • メーカーズマーク赤

です。バーボンは、初夏に一本飲むくらいですが、今年もやっぱりマークでした。マークも、バーボンの中では特有の香りを持つ酒なので、なんかブドウっぽいさけばかりですね。春だし、それもいいのではないでしょうか。

マンネリ化してますね。来月はもう少しがんばりましょう。

ひとまず完成

一ヶ月の歳月を経て、ようやくできあがりました。

簡単にいうと、やることのリストを毎週チェックして、Evernoteにアップロードできるソフトです。

よく言われるGive a milkというやつですね。とにかく、やることをリストアップしておいて、やったかやってないかを確認し、Evernoteにアップすれば、これまでにどのくらいやった/やってないを集計できます。

まぁ、そこまで凝らなくても、毎週リストをリフレッシュしてくれます。

というわけで、Evernoteのサポートに、APIキーをサービスしてるサーバでも使えるようにしてくださいね、というお願いのメールを出しました。

まだ、見かけはあまり手を入れてないのですが、基本的な部分はちゃんと動くはずです。AsynkTaskでのアップロード、パスワードの暗号化、などなど、必要な部分はきちんと作ってあります。

最後になって広告を24時間に一度はロードしなさい、とAPIの仕様書に書いてあってビビりましたが。ただ、テストしてる限りは読み出さなくても使えるようでした。現在地震の関係でEvernoteのアカウントは全部プレミアム扱いなので、もともと読まなくてもいいのか、今限定でチェックが機能していないのかはよくわかりません。

そして、次のネタを探しているのですが、これだ!というのは今のところなくて、とりあえず歓送迎会シーズンに向けて割り勘のアップデートでもしようかなと思っています。

(実はもう手をつけた)

2011年3月17日 (木)

震災随想

ここのところ電車が動いていないので会社には行っていません

で、Androidを黙々と作っているのですが、そっちのほうは主にバグとりなのであまり提供できる話題がありません。

地震ですが、原発が大変なことになっているようですね。ここは原発から100kmちょっとあるのでしょうか、まぁ、ここまで影響があるなら日本中どこもだめなので気にしても仕方がないでしょう。

被爆、という点では、心臓止まりそうになったことがある身としては、まぁ、人生そういうこともあるよね。という感覚です。心臓の血管がつまるとカテーテルという針金を入れて治すわけですが、針金の入り口は足の付け根か手首です(必要な針金の太さでどこから入れるかが違います)。
で、どこを通っているかは外からは見えないので、X線で様子を見ながら通していきます。
ええ、レントゲン写真のX線です。それを当てっぱなしにするのです。よくテレビで言っている、年間の被爆量とかはあくまでも検査に使うための限度であって、今にも死にそうな人にはそんなものは関係ありません。レントゲンは一瞬出すだけですが、こっちは出しっぱなしです。何倍かなんて考えるだけ無駄です。針金が心臓まで届くと、血管をさらによく見るために造影剤という薬品を(血管に)入れます。察しのいい方はお気づきでしょうが、これがヨウ素です。ヨウ素には放射線を吸収する性質があって、陰になるからよく映るのです。検査する前にね、後遺症出たりするかもしれないとか、色々かいた承諾書という書類にサインします。でも、これまた今すぐお迎えが来るかもしれないひとにとってはただの紙です。でも、そこら辺に飛んでる放射性物質からの放射能のために飲むのはどうなんでしょうね。リスクのほうが大きいんじゃないんですかね。

そんなこんなでここ数年間で3回ほどカテーテルをやり、今出回っている放射線なんて目じゃない量を浴びて、ヨウ素も結構取り入れたと思います。それでも今のところ影響は出ていません。ま、なるようになりますよ。

2011年3月13日 (日)

地震(2)

前回のあらすじ

東京のオフィスで一晩すごし、24時間かけて帰ったきたところは自己中のスクツだった。

13日10時ころ (つづき) パン屋はあきらめて、隣のホームセンターに行く。バーナーの着火材とホワイトガソリンを補充したかった、というのもあるが、まず、店は駐車場は満車。屋上なら空きがあるとアナウンスされているほどの大混雑。その人たちが何を目当てに来たのか見たかった、というのが本音。キャンプ用品コーナーのうちでもガスボンベだけは略奪にあったような有様でものがない。目的のものは(最近はガソリンのバーナーなんてはやらないのか)売っていないようなのであきらめて帰ることにする。ちなみに、他の人たちはというと、トイレットペーパーや、灯油をたくさん抱えている人をよく目にする。うーん、オイルショックからぜんぜん学習してないじゃんか。仕方ないので街を散歩する。飲食店は断水のため休み、その他の店舗は危険だから休み。街はほとんど機能してない。駅へも行ってみたが、そこで見たのは、代行バスという人間押し寿司製造機。そりゃ、2000人以上を時速100km以上で運べる電車の能力を100人かそこらしか乗れないバスで代行なんてそもそも無理ですがな。今日は連絡通路にはロープを張ってなかったが、駅舎には説明の駅員が2人いるだけ。ええとさ、オレが昨日買ったグリーン券の払い戻しはいつやってくれるのかな?そして、運休の掲示と代行バスの案内。どうやら、あの押し寿司製造機は600円とるらしい。駅員が他の人に運休の理由を説明している。線路が曲がっているとか。しかしさ、取手までは動かせたわけで、その先は電車さえ片付けない理由は何なの?

13日12時ころ とりあえず、飯を炊く。冷蔵庫を見たら納豆があった。まぁ、非常事態なわけで、あったかい飯がくえれば文句を言ういわれはない。けど、パンを買えなかったので、このままだと明日も納豆定食になってしまう(明日は月曜で、会社はやってるはずだけど、なにせ、電車が家の前に乗り捨ててあるような状況なので、出社するのは無理。休む前提で行動中)。ホームセンターとは反対側にある、スーパーへ行くことにする。ここも、混んでいる。なんか、やけに米を買ってる人が目に付く。中を回ると、調理品はガスが出ないからということでコーナーお休み以外は普段とそれほど変わらない。が、野菜だとかだれも見向きもしてない。私といえば、普通に売り場を回る。大粒の女峰がうまそうだったので、買う。肉類はというと、鶏肉はほとんどなし、豚や牛もひき肉や、薄切りの、調理に手がかかるようなもの以外はほとんどなし。牛の肩ロースを安売りしていたので牛丼でも作ることにして、たまねぎを一緒に買う。帰りがけに、隣の花屋を見てみると、ちょうどつぼみがいっぱいになったジャスミンを売っていたので一緒に買って帰ることにする。米やらトイレットペーパやら大量に抱えた人が車に荷物を積むなか、リュックサックと片手にジャスミンの鉢ぶら下げた中年男が歩いていく様はなかなかシュールだ。

13日15時ころ 防災無線から、給水を開始したという案内が放送される。けど、このあたりはまだのようだ。まぁ、水がなければビールを飲めばいいじゃない。ということで、どうせ車は動かさないし、ビールを飲むことにする。この間買った、今年最後の琥珀エビス、まだ飲まずに残っているのでそれにする。

13日20時ころ なにかトイレで物音がするとおもったら、タンクに注水する音だった。給水が始まったらしい。これで、電車以外はほぼ正常とおり。散歩の時に駅で見た様子だと、どうやらJRは1週間程度は回復させないかもしれない。どうせ余震でまたレールが曲がるんだから、落ち着くまで電車はあそこに路駐しておくつもりなのだろう。切符切られることもないし。そうなると、会社にはいけないけど、まぁ、いいや。

(気が向いたらつづく)

地震

ようやく身の周りが落ち着いたのでこれまでにあったことをメモ代わりに書いておきます

11日15時ころ 会社にて。突然ビルが揺れる。2度目の揺れでビル外へ退避するよう案内が放送される。(しかしね、中に居るほうが安全だよ。鉄骨造のビルが倒壊するような事態なら、外はビルから降ってきたガラスの破片と燃える自動車の地獄絵図だよ。)外に出たものの、することはない。とりあえず、コンビニでお金を下ろしておくことにする。いざとなったら、キャッシュカードなんてただのプラスチック片にすぎない。

11日17時ころ 帰宅してもいいという連絡があったので、帰宅できるものなのか調べてみる。電車。ほぼ全部止まっている。首都高、いつものように6号線は渋滞してるものの、三郷から先は一部規制(速度規制?)だけらしい。とりあえず、目の前にある総武線は動く気配もないので、運転再開を期待して上野に向かって歩くことにする。。さすがに、自宅まで60kmを夜中に歩く気にはならないが、上野に向かえば何とかなるのではないかと思って歩き出す。

11日20時ころ 道をすこし間違えたこともあって上野に着いたのは出発してから2時間以上後だった。しかし、上野駅はシャッターを降ろして閉鎖されている。当然、あたりのホテルは既に満室。こんなことなら、道中で宿を確保しておけばよかった。しばらく策を練ってみたが、いい案は思い浮かばない。近くにある、自社の別オフィスに行くことにする。

11日21時ころ オフィスに着いた。入るには身分証明書が要るらしい。自分は当然入れたけど、かなり寒くなっているのに、コンクリートの上で行き場もなく座っている人を気の毒に思いつつ、でも、なにもしてあげられずビルの中へ。(こういうときくらいビル開放すればいいんじゃないの?)椅子を並べて夜をすごす。

12日6時ころ テレビで、電車が7時から動く予定という報道があったので上野駅へ向かう。が、一向に電車が動く気配なし。線路上に止まっている電車を駅まで移動させているといっている(え?電車止まってから12時間以上、なにもしてなかったの?)

12日9時ころ ようやく最初の電車が動く。しかし、当初、我孫子までしか行かないとアナウンスされていたが、取手まで行くという。途中まで行ってもしょうがないと思って並ばなかった。でも手遅れ。で、次の電車に乗るための列に並ぶ。が、当初、通常の3~5割といっていた輸送能力は、実際は1時間に1本の電車を動かしただけ。次にきた電車に乗るが中は当然スシ詰め。気分悪くなる人続出。なんとか寿司になる前に取手についた。

12日12時ころ 家に帰るためにタクシー乗り場に並ぶ。が、タクシーは待てども待てども来ない。駅にお迎えに来る自家用車で駅周辺は渋滞していて、結局だれもたどり着けない。そして、たまに取手以北から(取手から上り電車に乗るために)客を乗せてきたタクシーも来るが、駅のタクシー乗り場の客は乗せない。たぶん、鑑札がないから。しばらくウロウロして、並んでない客を拾うかして、去っていく。そして、たまに鑑札を持ったタクシーが戻ってくる、が、延々待っていたはずの人は、自分が乗れれば十分、と一人だけ乗り込んでタクシーはまた去っていく。結局、10人ほど前の人が相乗りを誘ってくれてそれに乗って帰ったのは16時ころ。自宅近くの駅で車を降りる。駅は連絡通路さえロープで閉鎖していた。近くのラーメン屋でラーメン食べて帰る。

12日17時ころ 家についた。棚に積み上げたCDが床に散らかっている以外は、特に大きな変化はなし。想像していた状況のうち、一番軽度。CDを積みなおして(?!)平常とおり。と思ったが、停電したらしく、PC類はいろいろ止まっている。ガス、電気は来ているが、水道は断水している。え、さっきのラーメン屋はそんな風ではなかったけど。PCの電源を入れるが、インターネットは接続できない。親類へ生きてるとだけ電話して、今日はさっさと寝ることにする。

13日6時ころ 一応、各部屋を回って、ドア、窓の類が開閉できることを確認する。庭にでると、なんと、外の線路に電車が乗り捨ててある。もしやと思って近くの踏み切りに行くと、踏み切りは電車が塞いでいる。ふざけるなJR。もう、丸1日以上経っているのに、電車10m動かすこともやってないのかよ。インターネットの情報によると、水道は給水再開作業中で、近くの小学校に給水車が居るらしい。リュックサックに水筒を入れ、自転車こいで小学校へ行く。が、そこにはポリタンクをたくさん抱えた人たちが。え?煮炊きに使う水があれば十分じゃないの?案内の人が、学校の水道から水を汲んでいいと教えてくれたので、ポリタンクの列には混じらず水筒に水を満たす。途中、どこのガソリンスタンドも売り切れで閉店だった。どうやら、パニックになってスタンドに押しかけたらしい。その、貴重なガソリンでやることといったら、使いもしない水を溜め込むこと。それ、おかしくないか?

13日8時ころ 状況が悪化した時に備えて、ガスのランタンとバーナー(料理に使うコンロね)の確認をする。バーナーはどうも火の勢いがよくない。やっぱり、気化器が詰まってるらしい。一度、焼いてつまりを取らないといけない。ランタンは快調。とりあえず、沸いたお湯でコーヒーを淹れて飲む。なんか、鳥がいつもよりたくさん居る気がする。

13日10時ころ 散歩がてら、いつものパン屋に行くことにする。自家製のパンを売っていて、高めだけどおいしいから毎週買っている。ああ、やってる。と思うが、中に入るとすごい人。そう、コンビニを食い尽くした人達が押し寄せている。とりあえず、明日は定休日だけど空けてくれることを期待して今日は家で米を炊くことにする。明日になれば輸送も水道も回復し、あの人達はどこかもっと安くで食を提供してくれる施設へ行ってくれるだろう。

(つづく)

2011年3月 7日 (月)

中華PadとEthernet

今日は雪でした。

日中に降ったにもかかわらず、北関東のこのあたりでも数センチ積もりました。

さて、中華Padネタです。

実は、私が持っている中華PadにはUSBのAコネクタが付いています。ここにイーモバイルなとが出しているUSB型の通信アダプタをつないで使うのが目的のようで、サイトにも3Gアダプタが使えると書いてあります。そのほかに普通にUSBメモリも認識できます。どうやら、キーボードもつながるようです。(試してはいません)

さて、今回のネタは、ここにEthernetアダプタつないだらどうなるの?です。

それが、動くんです。まぁ、動くと使えるの間には結構な距離がありますが。

今回試したのは、PlanexのNE200TX-G2というアダプタです。往年の名機、NE2000を彷彿とさせるネーミングです。まぁ、たぶん名前のとおり、NE2000コンパチのチップをUSBドライバにくっつけたものでしょう。実はNE2000現物も持っていたりするのですが、NE2000といえばISAなアダプタなわけです。DMAなどとは無縁の、のどかな時代の製品です。その上、NE2000はストリング命令でスピードを稼ぐ構造になっているのですが(今では当たり前ですが、その当時は画期的でした)USBを経由すればストリングもへったくれもありません。つまり、このアダプタも動くには動くのですが、かなり遅いです。それでもやっぱり、日本においては認定を通ってない無線LANは違法無線局なわけで、堂々と通信できるというのはメリットがあります。

Linux 2.6.29あたりでWiMax(IEEE802.16)もサポートされたはずなので、実はWiMaxのUSBアダプタもうごくんじゃない?と思っているのですが、さすがにこれはだめもとで試すというわけには行きません。貸し出しをしてくれるようなので、時間ができたら借りてみようかなと思っていますが。(貸し出しはエリア内かどうかを試すためのものらしいですが、堅いこといいっこなしで)

なお、USBポートにアダプタを差すと、構成変更が起きてすべてのアプリが止まるようです。お試しになるときにはご注意ください。

ひさしぶりにアレ、行ってみたいとおもいます。そう、Amazonのアレ。でも、中華Padに低速Etherアダプタ。こんなアフィリエイトばかり書いてるから売り上げないんだよね。

2011年3月 6日 (日)

砂場遊び

開発もそろそろ折り返しくらいのはずなんですが、進捗ははかばかしくありません。

結局、土日で進んだのはサーバへアップロードする処理をほんの少し書けただけです。

というのも、サーバがxmlを受け取ってくれません。せめて、どの部分が気に入らないか教えてくれればもっとデバッグがはかどるような気がするのですが、例外の中に理由として Content is not allowed in prolog. といってくるだけで、まるで木で鼻をくくったような対応です。

デバッグがはかどらないもうひとつの理由として、Webクライアントしかsandboxにつなげられない、というのがあります。Android上のアプリでsandboxへつなげないので、ちょっとしたこと、たとえばノートを引っ張ってきて表示する、とかでもプログラムを書かなければならず、それもすぐに動くわけではないのがかなりネックになっています。何をするにもログインからはじめなければいけないので。

まぁ、愚痴を言っていても仕方ないのであせらずいきましょう。砂場遊びなのか、砂に遊ばれているのか。sandboxといえば、SecondLifeでスクリプト書いていたのを思い出しますね。規模は違えど、あれもJava風の言語だったように思います。もうほとんど忘れてしまいましたが。

現在は動くかどうか確かめる段階なのでまだバックグラウンド処理にしていないのですが、そういったこともやらなければいけないし、先は長そうです。

ところで、以前公開したアプリですが、ようやく100ダウンロードを超えました。約1ヶ月で100。うーん、決して多いといえる数ではないですね。メディアで紹介されなければこの程度で普通なのでしょう。やっぱり、探しつらいですよね。できれば、3月、4月の宴会シーズンに一度アップデートして目立つところに並べたいのですけどね。

日本語版のアプリのマーケットを立ち上げようという動きもあちこちであるようですが、私については静観しています。もっとさ、世界で勝負して、夢だけでも大きく持とうよ。

2011年3月 5日 (土)

暗号の一覧

なんか、最近気の利いたタイトルを思いつかなくなりました。

で、暗号です。今回の場合、ログインするためにIDとパスワードが必要になります。パスワードについては、いくら本体のメモリに格納するとはいえ、平文のまま格納するわけにはいきません。

そこで、暗号化するわけですが、暗号処理を呼び出すのにはそれなりの手続きを踏まないといけないようです。

最終的にはCipherというクラスを使ってデータを暗号化するのですが、この暗号処理を起動するには暗号化(もしくは複合化)鍵が必要になります。
複合化鍵はどうやって作るかというと、KeyFactoryというものを呼び出して、KeySpecというものを指定して作ります。そして、そのKeyFactoryとかKeySpecとかいうものはProviderという中に格納されています。

なんですが、実際には暗号のやり方は何通りもあって、このそれぞれの手順で”どの”暗号を使うのかを具体的に指定する必要があります。自分で作って戻すならAESが使えれば十分なんですけどねぇ。

Developerサイトもよく探したつもりなのですが、具体的にこうすればいいというのが無いんです。例としてあがっている"AES/CFB8/NoPadding"という指定さえ、そんなんしらね。といわれてしまいました。(これは8ビットのストリーム型という指定で、つまり、1バイト平文を入れたら1バイト暗号文が出てくる形式だと思われるのですが)

仕方ないので、一覧を表示するアプリをつくりました。当初、Logで出していたのですが、膨大に出力される上、Typeが混在していてなにがなにやら分からない有様なのでSpinner使ってListを選択できるようにしました。

数時間Eclipse先生とお話して、こんなんでました。やっつけ感たっぷりです。結構長いです。

上のSpinnerがProviderの選択。下のSpinnerがType(KeyFactoryとかAlgolithmとか)の選択です。実際はProviderはBCを見れば十分なのですが、とりあえず、何が入っているのか分かるように全部出してみました。TypeのSpinnerはスクロールするほど項目が多いです。が、それを選択した状態でもリストがスクロールするほど名前があります。

あー、enableFilterすればよかったのかな。 時間ができたら考えます。

public class cipherlist extends ListActivity {

      private final String TAG = "cipherlist";
      
      private Context mContext ;
      private LayoutInflater mInflater ;
      
      private Spinner mProviderSpinner ;
      private Spinner mTypeSpinner ;
      private ListView mListView ;

      private Provider mProviderArray[] ;
      private int mProviderSelected ;
      private String mProviderName ;
      private String mMatchType ;
      private ArrayList<String> mTypeArray ;
      private ArrayList<String> mAlgolithmArray;
      private ArrayList<String> mClassNameArray;
      
    public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
        setContentView(R.layout.cipher);
   
       mContext = getApplicationContext();
      
       mProviderSpinner = (Spinner) findViewById(R.id.spinner1);
       mTypeSpinner = (Spinner) findViewById(R.id.spinner2);
       mListView = (ListView) findViewById(android.R.id.list);
      
            mProviderArray = Security.getProviders();
        ArrayAdapter<Provider> pAdapter = new ProviderSpinnerAdapter<Provider>(mContext,
                  android.R.layout.simple_spinner_dropdown_item, mProviderArray);
       mProviderSpinner.setAdapter(pAdapter);
       mProviderSpinner.setOnItemSelectedListener(mSListener);
    }
    public void onResume() {
      super.onResume();
      
    }
    public void onStart() {
      super.onStart() ;
      
    }
      class ProviderSpinnerAdapter<Provider> extends ArrayAdapter<Provider> {
            Context mContext ;
            LayoutInflater mInflater ;
            int mLayoutId ;
            Provider[] mList;
            public ProviderSpinnerAdapter(Context context, int textViewResourceId,
                        Provider[] objects) {
                  super(context, textViewResourceId, objects);
                  mContext = context ;
                  mInflater = LayoutInflater.from(mContext);
                  mLayoutId = textViewResourceId ;
                  mList = objects;
            }
      public View getDropDownView(int pos,View cv,ViewGroup parent){
            if(cv ==null) {
                  cv = (TextView) mInflater.inflate(mLayoutId, null);
            }
            ((TextView) cv).setText( ((java.security.Provider) mList[pos]).getName());
            return cv;
      }
      }
    private OnItemSelectedListener mSListener = new OnItemSelectedListener(){

            @Override
            public void onItemSelected(AdapterView<?> parent, View v, int pos,
                        long row) {
                  mProviderName = (String) ((TextView)v).getText();
                  mProviderSelected = pos ;
                  Log.d(TAG,"mProviderSelected ="+String.valueOf(mProviderSelected));
                  setup_TypeSpinner();
            }

            @Override
            public void onNothingSelected(AdapterView<?> arg0) {
                  // TODO Auto-generated method stub
                  
            }
      
    };
    private void setup_TypeSpinner(){
      mTypeArray = new ArrayList<String>();
      mAlgolithmArray = new ArrayList<String>();
      Set <Service> ss = mProviderArray[mProviderSelected].getServices();
      Iterator<Service> ite = ss.iterator();
      for (boolean ended = false;!ended;){
            Log.d(TAG,"iterat");
            try {
            Service s = ite.next();
            String type = s.getType();
            mAlgolithmArray.add(s.getAlgorithm());
            boolean found = false ;
            for (String t: mTypeArray){
                  if(t.equals(type)) found = true ;
            }
            if (!found) mTypeArray.add(type);
            } catch (NoSuchElementException nse){
                  ended=true ;
            }
      }
      mTypeSpinner.setAdapter(new ArrayAdapter<String>(mContext,
                  android.R.layout.simple_spinner_dropdown_item, mTypeArray));
      mTypeSpinner.setOnItemSelectedListener(mTListener);
      mListView.setAdapter(new ArrayAdapter<String>(mContext,
                  android.R.layout.simple_list_item_1, mAlgolithmArray){
            
      });
    }
    private OnItemSelectedListener mTListener = new OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View v, int pos,
                        long row) {
                  mMatchType = mTypeArray.get(pos);
                  select_list();
            }

            @Override
            public void onNothingSelected(AdapterView<?> arg0) {
                  // TODO Auto-generated method stub
                  
            }
    };
    private void select_list() {
      mAlgolithmArray = new ArrayList<String> ();
      Set <Service> ss = mProviderArray[mProviderSelected].getServices();
      Iterator<Service> ite = ss.iterator();
      for (boolean ended = false;!ended;){
            Log.d(TAG,"iterat");
            try {
            Service s = ite.next();
            String type = s.getType();
            if (mMatchType.equals(type)) {
                  mAlgolithmArray.add(s.getAlgorithm());
            }
            } catch (NoSuchElementException nse){
                  ended=true ;
            }
      }
      mListView.setAdapter(new ArrayAdapter<String>(mContext,
                  android.R.layout.simple_list_item_1, mAlgolithmArray){
      });
    }
}

2011年3月 1日 (火)

例外処理

さて、例外処理です

とりあえず、例外って何?というのは端折ります。で、例外がおきたらどうするか、ですが、
(1)その場でcatchする。
(2)classにthrows指定をつけて呼び出し元に届ける

があるわけです。(1)は、できるならそうしたいのですが、AndroidにはViewに触ることができるのは(最初に起動された)UIスレッドだけ。という制限があり、バックグラウンドからはTextViewにメッセージを書くことさえできないので、その場で全部対処とは行かない場合があります。
(2)はというと、UIへ持ち帰るまでにどこで何が起きたのか分からなくなってしまい、細かな処理はできません。それに、catch()がいっぱい並んでプログラムの見通しが悪くなります。

じゃぁどうするの?という問いに対する答えがHandlerです。インタフェースなのですが、Handlerというのを呼び出すと、指定のRunnableをUIスレッドのキューにつなげてくれます。つまり、これを使えば別スレッドからViewを操作する(実際は依頼する)ことができます。

では、昨日のプログラムを修正して実際に動かしてみます。まず、AsyncTaskの中のdoInBackgroundにtry-catchをつけます。そして、処理の最後に”わざと”例外を発生させます。まさにマッチポンプです。

例外が発生したらHandlerの出番です。何をするかというと、事前に用意しておいたViewGroupの中に、これまた事前に用意したButtonをaddViewします。これで、例外がおきると画面にボタンが1個出現するようになります。

そして、このボタンはどうなっているかというと、クリックされると自分を画面から消して、リストを読む処理を再度実行させます。(この名前はrefreshとなっていますが、実際はListViewの中身は消していないのでListはどんどん伸びていきます。その辺はご愛嬌で)

こうして、最後までリストする。リトライする。を、ボタンを出したり消したりしながら繰り返します。

とりあえず、機能の試験実装ということで、こうすれば動く、レベルですが、ほとんどのアプリがなんらかのネットワーク接続を使うことを考えれば、部品化すれば再利用頻度の高い部品になりそうです。

ソースはちょっと掃除しました。それと、refresh()ですが、これはボタンのonClickアトリビュートから呼んでいるのでリスナの設定はありません。こういう、ちょっとした検証には便利ですね。それに、今回はretry用のボタンはコードで生成していますが、実際のアプリではこういったものもxmlからインフレートすることになるので、その際にonClick使えばプログラムがすっきりするかもしれません。

public class asyncTaskSample extends ListActivity{
    static final String TAG = "AsyncTaskSample";
    static final String SDCard = "/sdcard";

    private Context mContext ;
      
    private Button mRetry ;
    private ViewGroup mParent ;
    private Handler mHandler ;

    private ArrayAdapter mAdapter ;
   
    public void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
        setContentView(R.layout.asyncsample);
   
       mContext = getApplicationContext();
       mInflater =LayoutInflater.from(mContext);
      
       mHandler = new Handler();
      
       mAdapter = new ArrayAdapter<String>(mContext, android.R.layout.simple_list_item_1);

        setListAdapter(mAdapter);
      
       mParent = (ViewGroup) findViewById(R.id.linearLayout1);
       mRetry = new Button(mContext);
       mRetry.setOnClickListener(mListner);
       mRetry.setText("Retry");
    }

    private class DirScan extends AsyncTask<String, String, Void>{
            @Override
            protected Void doInBackground(String... dir) {
                  try {
                        File sdir = new File(dir[0]);
                        File[] filelist = sdir.listFiles();
                        for (File file : filelist){
                              publishProgress(file.getName());
                              SystemClock.sleep(500);
                        }
                  throw new Exception();
                  } catch (Exception e) {
                        mHandler.post(r);
                  }
                  return null;
            }
            protected void onProgressUpdate(String... foundDir){
                  mAdapter.add(foundDir[0]);
                  mAdapter.notifyDataSetChanged();
            }
            protected void onPostExecute(Void nil){
                  Toast.makeText(mContext, "onPostExecute", Toast.LENGTH_SHORT);
            }
    }
    public void refresh(View v){
      new DirScan().execute(SDCard);
    }

    private OnClickListener mListner = new OnClickListener(){
            public void onClick(View v) {
            mParent.removeView(mRetry);
                  refresh(v);
            }
    };

    private Runnable r = new Runnable() {
      public void run(){
            mParent.addView(mRetry);
      }
    };
}

« 2011年2月 | トップページ | 2011年4月 »