2014年9月8日

[android]ProgressDialogのsetMessage()は、なんでresidがないんだろう?

純粋な疑問だ。

NFCのタグを待っている間、くるくるを出そうと思った。
サンプルを見ると、setTitle()やsetMessage()に文字列を設定している。
まあ、サンプルだからそういうもんだよな、とR.string.fooみたいなのを設定すると、setTitle()はいけるのに、setMessage()はエラーになった。

そういう実装なんだから、仕方ないと言えば仕方ないんだけど、なんでだろう?
show()なんかも、ことごとくCharSequenceを引数に取ってるし。
そういえば、AndroidはいつからかBuilderで作るようになってたな、と思い出した。

 

ProgressDialog.Builder builder = new ProgressDialog.Builder(this);

こんな感じでやっていくと、setMessage()もresidを受け付ける。
よしよし、と、最後にcreate()でProgressDialogにキャストし、SPINNERの設定をさせた。
さて、実行!

・・・キャストできません、と落ちた。
キャスト失敗って動的な例外なんだ、という感想はさておき、なんでだめなんだろう?
ProgressDialogはAlertDialogの派生だから、キャストしても悪くなさそうなのに。
(だから静的にはエラーが出てないんだろうけど。)

使いたければ使えばいいし、カスタマイズするなら自分でやればいいじゃないの、というスタンスなんだろうか・・・。

[android]基底クラスで今のActivity名を知りたい

NFC関係の画面を作っているのだが、いくつか作っていると、だいたいどの画面でも同じようなことを書いていることに気付いた。
onResume()でenableForegroundDispatch()して、onPause()でdisableForegroundDispatch()して、あとはインテントが飛んできたときに処理する・しないを書いて・・・。

えーい、こういうときはActivityの派生クラスを作って、そこにまとめてしまおう。
abstractでextends Activityなクラスを作って処理をまとめた。

それはよかったのだが、「このActivityのときはやらないけど、それ以外ではやる」というのがあった。
タイトルバーをタップしたときに、一番上のActivityに戻る処理だ。

if (!getLocalClassName().equals(MainActivity.class.getSimpleName())) {
    getActionBar().setHomeButtonEnabled(true);
    getActionBar().setDisplayHomeAsUpEnabled(true);
}
Activityが提供しているのが、一番下のクラス名を返すAPIだったので、それにあわせている。
同じActivity名で別パッケージが存在すると区別はつかないのだが、まあこれでいいや。

2014年9月7日

[android]<string>の中に@stringが書けるんだ

Androidで設定画面を作っていた。
もう、ネットがないとまったく何も作れないレベル・・・。

それはともかく、設定なので、値がある。
これは、SharedPreferenceでよい。
その初期値をどうやって設定すればよいか悩んでいた。

ListPreferenceでやってるのだが、中にandroid:defaultValueを書いておくと、それが初期値になると言うことはわかった。
しかし、設定画面を呼ばずに使うこともあるので、未初期化の値があれば初期値を設定したい。
そんなときは、

PreferenceManager.setDefaultValues(this, R.xml.preference_settings, true);

みたいに書けばよいこともわかった。

何を悩んでいたかというと、defaultValueの設定が文字列だったことだ。
ListPreferenceで画面に見せる値はandroid:entriesで、実際に設定する値はandroid:entryValuesに書いていて、それらは@arrayで指定している。
だけど、defaultValueに設定するのは文字列なので、「@arrayの一番上」みたいな指定ができないようなのだ。
かといって、このXMLの中に直接文字列を書くのも、格好が悪い。
じゃあ、@stringの方に「デフォルト値」みたいな文字列をわざわざ用意するというのも、あまりきれいでない気がした。
もし@arrayの値を書き換えてしまったときに、デフォルト値への反映をし忘れそうな気もする。

ああもう!とやけになって、<string-array>の中に@string形式で書いたら、あら、動くやん。

    <string name="felica_key1">xxyyzz</string>
    <string name="felica_key2">112233</string>
    <string name="felica_key3">aabbcc</string>
    <string-array name="felica_master_keys">
        <item>@string/felica_key1</item>
        <item>@string/felica_key2</item>
        <item>@string/felica_key3</item>
    </string-array>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="@string/settings_category_id">
        <ListPreference
            android:key="pref_master_key"
            android:entries="@array/settings_master_key"
            android:entryValues="@array/felica_master_keys"
            android:defaultValue="@string/felica_key1"
            android:title="@string/settings_master_key"
            android:dialogTitle="@string/settings_master_key" />
    </PreferenceCategory>
</PreferenceScreen>

 

いやー、まだまだわかってないですな、私・・・。

[android]ForegroundDispatchだけやりかった

enableForegroundDispatch()を呼ぶと、そのActivityに対してNFCのintentが飛んでくる。
私がやりたかったのは、ボタンを押したときだけintentを受けとって、それ以外は受けとりたくなかった。
が、だからといって、他のアプリが立ち上がってきても嫌だ。
つまり、ForegroundDispatchはするけど、intentを飛ばしてほしくない、ということをしたかった。

 

よくわからんなりにいろいろやってみたが、最後は「intentが飛んでくるけどアプリで捨てる」ということにした。。。
だって、どうやってもダメやったけん・・・。

「タグが見つかった!」というときの音がするので、ユーザにToastなどで違うということを説明せんといかんのはあるが、まあ、それはそれでよいか。

2014年9月6日

[android]Nexus7 2012でシステムコードが取れないがわからん

わからんシリーズだ。

うちには、Nexus7 2013(5.0α?)と、Nexus7 2012(4.4.4)がある。
FeliCa Liteをかざして見るのだが、なぜかシステムコードが取れない。
しかも、4.4.4(API 19)の方だけ。
why ?

(Tag)intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)

こんなのでTagを取ってきて、

NfcF nfcf = NfcF.get(tag);

こんな感じでNfcFを取ってくる。
そうすると、中の方で

/** @hide */
public NfcF(Tag tag) throws RemoteException {
    super(tag, TagTechnology.NFC_F);
    Bundle extras = tag.getTechExtras(TagTechnology.NFC_F);
    if (extras != null) {
        mSystemCode = extras.getByteArray(EXTRA_SC);
        mManufacturer = extras.getByteArray(EXTRA_PMM);
    }
}

こんなのが行われて、mSystemCodeにデータが入ってくる。
・・・のだが、そこで0が入った配列が渡されてくるので、システムコードが取って来れないのだ。
ちなみに、IDmもだめだった。
こっちは、0x00じゃなくて、ちょっとゴミっぽい。

不思議なことに、NfcFはそうなんだけど、元になっているTagの方にはIDmもシステムコードも入っていた。
だから、Intentから取ってくればいけるのかもしれん(試してない)。
が、せっかくNfcF#getSystemCode()ってのがあるのに使えないのは、なんか変だ。

うーん、わからん。

2014年9月5日

[android]Android Studioでソース管理しなくてよさそうなものはどれだ?

特に、使い勝手がよいとか悪いとかの感想もなく使っているAndroid Studio。
基準にできるものが何も無いので、便利なのか不便なのかもわからない。
「Ctrl+Alt+Lで全ファイルのimport整理ができるのが便利」とは思うけど、Eclipseのときにできなかったかどうかがわからないので、勝負にならん。。

さて、そういう状況の中で、ソースのビルドエラーが消えたので、とりあえずgitのリポジトリを作って保存しておこうと思った。
Android Studioの設定の中にgithubがあったので、githubだと連携が楽なのかもしれない。
が、ひねくれた私は、Bitbucketを使おうと思った。
いや、Bitbucketを使うのがひねくれているのではなく、連携してそうなgithubを使わない、という私がひねくれているのだ。
プライベートなリポジトリが使えるし、5人までなら無料なのよ。

そういうのはよいとして、Android Studioの場合、どれを管理して、どれを管理から外すべきかわからなかった。
これはよさそうな気はするが・・・これはダメそうな気がするし・・・。
あれこれ考えていたが、飲みながらやっていたので「全部やってしまえ!」でadd/commitしたところ、なんかうまいことなってくれた。
そう、.gitignoreファイルがあったので、それが除けてくれたのだ。

なーんだ。
悩んで損した。

2014年9月3日

[java]自分で60点くらいの例外処理を考えよう (3)

今回で最後。
60点どうのこうのいうよりも、Javaがわかってないし経験も足りないので、がんがん書いていって自分のスタイルをつかんでいくのがよいんじゃないか、という結論になりつつある。
でもね、こういう悩むって過程も必要だと思うのだ。悩まざる者、そこにあるのは死だ!みたいなことを、妖精王(山岸凉子)で言ってたように思うが、そんな感じ。

それに、ときどき立ち止まって振り返らないと。
技術なんてしばしば新しくなるから、自分がその時知っていた最善の技術が、今ではそうでもないこともあろう。
ほら、今見てるチェック例外だって、時代を経るに従って受け止められ方が違ってきてるみたいだし。
そういう変化があるから、技術はいつも新鮮で、そういうのが好きな人達をつかんで離さないのだと思う。

というのもあるが、私が飽きてきた、というのが一番の理由だ。


あっさりと終わらせてしまいたかったのだが、どうしてもわからないところがあった。
これだ。

BasicTagTechnology(Tag tag, int tech) throws RemoteException {
    mTag = tag;
    mSelectedTechnology = tech;
}

Javaって、Operator Overloadはなかったはず。
つまりここで行われるのは、純粋な代入処理。
なぜ、RemoteExceptionのチェック例外が指定されているんだろう?

まず、コンストラクタでも例外は返してよさそうだ。
根拠って言う根拠は見つけてないけど、C++でもコンストラクタで返せるから、くらいか。いや、言語も思想も違うのはわかるんだけど、なんか、イメージとしてね。

BasicTagTechnologyクラスはabstractだから、継承する方もthrowsまで@Overrideしないといけないのかもしれない。
つまり、基底クラスにthrowsがついていなかったら、派生クラスにも付けられない、という考え方だ。
どうだ?

ってよりも、私がちょろっとやったとき(Personal Java)のときは、@Overrideなんてなかったぞ。
まあ、そんなことをいったら、私が入社した頃のコンパイラはCにvoidポインタがなかったけどな。


あ、@Overrideは不安なので確認しておこう。
onCreate()にthrows IOExceptionを追加すると・・・エラーが出た。
エディタ上に出たので、コンパイルエラーなのかどうかがわかってないけど、ダメだと思ってよいのだろう。

つまり、BasicTagTechnologyのコンストラクタでthrowsを付けたのは、「きっと子供たちの誰かがRemoteExceptionを返したくなることがあるはず・・・」という親の想いがあったからだろう。
それをありがたいと思うか迷惑と思うかは人それぞれだろうが、その子供たちもthrowsというくびきを背負うことになるのだな("くびき"って、頸と木で、十字架の意味?)。


で、疲れてきたので、しばらくこうしようと思う。

  • 標準で実行時例外があるものは、知ってる範囲でそれを返すようにしよう。
    それをcatchするのは、引数に責任を持っている人。
    たとえば、A-->B-->Cの順番で呼ぶことになっていて、Aは引数無し、Bが引数付き、Cが引数で例外発生、となったら、Bがcatchする。
    Bが作った引数にAが関与しているなら、Bがcatchして意味を持つExceptionに変換してAに投げる。
  • チェック例外は、継承した人達が使いそうなら、とりあえず付けておく。
    そこまで先がわからないなら、付けない。
    チェック例外を受けた方は、なるべく早めに他の例外なり戻り値なりに変換した方がよい。チェック例外はけっこうどうしようもない例外が多いので、上に行けば行くほど「なんのこと?」になってしまうと思うから。

かなり曖昧なところは残るのだけど、とりあえず文章にすることができたのがよかった。
仕様書はいらん、となることもあるかもしれんが、文章で説明できないことを口頭で説明できるとも思えない。
文章で説明して、足りないところは口頭で説明して、ダメなら会って説明して、その結果をまた文書に反映して、というのを繰り返して、ようやくできあがるものもある。

ネットの時代だ、とかで大規模開発が時代遅れっぽく見られることもあるけど、やはり工数がかかって、人数も多く必要で、という開発は存在するだろう(そういう開発しかやったことがないけど、ネットに出ないだけだと思う)。
工数がかかって人数が少なめでよい場合はいいんだけど、だいたいそういうときは「金に糸目は(あまり)つけないから、ばんばん人使って開発しろー」ってときだと思う。
そうなると、人-人の意識伝達を早くするため、プロジェクトの最初の方で「こうやりましょう、こんな感じでやりましょう」という大きな方向付けがあるだろう。
100人すごい技術者をそろえても、各人が1時間まごまごしたら、100時間無駄になるのだ。その「まごまご」は、各人のせいではなく、管理者の責任になる。
そうなると困るので、まずは100人が直接自分と対応するのではなく、グループを作ってもらい、そのグループ長とだけ接点を持つように考える。そうすると、まごまごの責任は分散できる(ということより、100人の相手はやってられない、という物理的な事情があるのだが)。
そうして、この責任を・・・

はっ、なんか、関係ないことまで書こうとしてる!!
ともかくここで言いたかったのは、例外処理について考えるだけでもこういうごにょごにょした議論を一人ですることになるんだから、お仕事で人に来てもらって作業するときは、そういう議論があることを前もって把握して、準備しておくべきだろう。

私は一人でJavaやっているので、基準作りは自分でやらないといけない。
そして、自分の書いたJavaのコードに、それぞれそれなりの説明を付けられるようになったとき、私も「初級Javaプログラマ」の称号を得ることができるのであろう!

まだ見ぬ、その日まで・・・。

2014年9月2日

[java]自分で60点くらいの例外処理を考えよう (2)

どうエラーを返すか、の前に、もうちょっとチェック例外のことを考えたい。
今作ろうとしているのは、NfcAクラスに対するMifareUltralightクラスのようなもので、そのFeliCa Lite版だ。

  • close
  • connect
  • get
  • getMaxTransceiveLength
  • getTag
  • getTimeout
  • getType
  • isConnected
  • readPages
  • setTimeout
  • transceive
  • writePage

赤文字が、追加されたメソッド。
NfcAにあるgetAtqa()なんかがないのは、NfcAを継承してないから。

TagTechnology > BasicTagTechnology、という継承になってるんだけど、BasicTagTechnologyはpublicがついてないから、私から使うことができない(のよね? 勝手にandroid.nfc.techなんてパッケージ名付けてもだめだと思ってるが)。


私が追加したいのも、こういうやつで、FeliCa Liteのブロックにアクセスする処理だ。
迷っているのは、こういう内部のAPIを使って機能を実現するやつ。
transceive()を使うけど、それはIOExceptionを投げる。
じゃあ、使ったAPIもIOExceptionを投げた方がよいか、隠蔽した方がよいか。

MifareUltralightクラスは、ほぼそのままにしている。
transceive()の返すIOExceptionもあるが、checkConnected()の返すIllegalStateExceptionもそのまま。
実行時例外はスルーして上まで投げて、チェック例外はやった方がよいなら自分でチェックするし、そうじゃないと思ったらthrowsして上に投げる、という判断なのかな。

 

圧倒的に、Javaソースを眺めている経験値が足りないですわ-。
今はAndroidのNFCのソースしか見てないという、視野が非常に狭い状況だから、またどっかで混乱するんだろうな。
まあ、足場がないと混乱もできないから、以前よりはよくなったと前向きに考えよう。

[java]自分で60点くらいの例外処理を考えよう (1)

たぶん、数日考えたくらいでは、Javaの例外を私がどうさばくのかという方針は決まらないだろう。
とはいえ、これに時間をかけているわけにもいかないので、自分の判定で60点くらいの納得ができればよいことにしてしまいたい。

最初の書いておくと、昨日の段階で自作のクラスはたいがいがExceptionを返すように作り替えてしまった。
勢いでやったと思うんだけど、やりすぎなのでまた作り方を変えるつもり。
その作り方を、ここで書きながら考えていこう。


まず、チェックされる例外、と、チェックされない例外、というものがJavaにある。
チェック例外・非チェック例外、検査例外・非検査例外など、呼び名は色々あるようだ。
英語だと、checked exception / unchecked exception、のようだ。

メソッド名の後ろにthrowsをつけ、そこにずらずら例外を書くのが「チェック例外」だと勝手に思っていたのだが、そうではないとのこと。
http://www.ne.jp/asahi/hishidama/home/tech/java/exception.html#h_checked_exception

チェック例外は、RuntimeExceptionを継承していない例外で、メソッド外に投げたいならばthrowsをつけてないとできないとのこと。
「そんな話から書くんか!」と思われるかもしれないが、知らないのだから仕方がない。

ようやく、定義らしきところにたどり着いた。
みんな「使い分け」の方に力が入っていて、そもそも何よ?が出てこなかったのだ。
http://docs.oracle.com/javase/jp/7/api/java/lang/Exception.html
はい、さっき書いたのと同じことが書かれてた。
関連するリンクに、Errorというのがあった。
http://docs.oracle.com/javase/jp/7/api/java/lang/Error.html
ここら辺まで眺めて、ようやくEffective Java 項目58の「checked exception / runtime exception / error」という分類になっているかがわかったというところ。

  • 非チェック例外はRuntimeExceptionを継承したもの。
    そうじゃないものはチェック例外。
    異常だというのがエラー。
  • 非チェック例外=実行時例外=RuntimeExceptionを継承しててthrowsなしでも伝わっていく。
  • チェック例外=throws書いていないと伝わらないので、書かないならtry-catchで解決する。

これでよいのかな?


抽象的なのは疲れるので、今やっているところの話にしたい。

今使っているAPIは、こんな例外を投げてくるようだ。

  • java.io.IOException
  • android.os.RemoteException
  • android.nfc.FormatException

継承図を見ると、どれもRuntimeExceptionが入っていないので、チェック例外ということになる。

例えば、NfcFクラスだと、以下が投げてくる。

  • close()
    呼ぶと、他でブロック状態になっていた要求がIOExceptionを投げられてキャンセルされる。
  • connect()
    既に他でconnectされていたら投げる
  • transceive()
    I/O失敗か、キャンセルされたら投げる

close()はthrows対象になっているのは、close()処理がブロックされているときに他スレッドでclose()されたときに投げるからかな?

上記の例外は、けっこうどうしようもない感じだ。
自分のあずかり知らぬところで処理がキャンセルされたり、transceive()だと・・・何だろう?

 

NfcFクラスでは出てこないけど、その基底になるBasicTagTechnology。
これが、けっこうRemoteExceptionを返している。
NfcFではこれが現れないので、NfcF内で吸収していることになる。

RemoteExceptionは、AndroidのBinder関係の例外基底クラスのようだ(その割りに、継承してる人がほとんどいない)。
でも、これはこれでどうしようもない気配が漂っている。

BasicTagTechnologyなんか見てると、transceive()でRemoteExceptionが発生するとIOExceptionに差し替えているし、close()なんかは発生してもログを出すだけだったりする。
もうどうしようもないから、普通の流れで変なことをせずに終わろうとしている、という感じか。


そんなわけで、まず1つの基準。
RemoteExceptionはどうしようもないから、上に返さないでエラーにしてあげよう。

じゃあIOExceptionはどうするか。
これも、なるべく隠してあげる方がよいような気がしている。
今作ろうとしているのはNfcFっぽいクラスなんだけど、TagTechnologyを継承してthrowsがついているところは、そのままにして、そうじゃないで自分で作ったメソッドなんかは、なるべくエラー値で返してあげようか。

そうなると、どうエラーを返すか、とか、引数エラーはどうするか、とかが出てくる。
今日は力尽きたので、次回にしよう。

2014年9月1日

[java]戻り値がよいか例外がよいか・・・

Cで作っていたライブラリを、Androidで動かそうと移植している。
まあ、NFC関係ですわ。
そうなると、Cでは戻り値でエラーを返していたところが、Androidでは例外で返すようになっているところがある。
そんなときは、例外をcatchしてエラー値に変換していた。

そしてエラーの戻り値としてenumを定義し、catchして変換をするようなことを書き始めて、ふっと思った。
「このまま例外で処理したらいいんじゃないの?」と。

例外を返すようにして、呼び出しの一番上でcatchするようにすると、ソースの見栄えが非常によろしい。

ret = proc1();
if (!ret) {
  return ERR1;
}
ret = proc2();
if (!ret) {
  return ERR2;
}

とか、

ret = proc1();
if (ret) {
  ret = proc2();
}
if (ret) {
  ret = ....

とかが、

try {
  proc1();
  proc2();
  proc3();
  ...
}
catch(xxxException e) {
  xxx;
}
catch(yyyException e) {
  yyy;
}

みたいになって、ロジックとエラー処理を別々に書ける。
まあ、エラーの種類によって動作を変えることもあるだろうけど、見通しはよくなると言うのはわかる。

 

そうなってくると、エラーが起きたときに、戻り値で返すのか例外で返すのか、がだんだんわからなくなってきた。
C++では、例外のコストがけっこう高いのと、組込みのC++は例外が外されてることもあったので、実はそんなに使ったことがない。
Cでは、gotoとラベルを使って、(ソースの見た目上は)似たようなしくみを実現しているのを見かけたことはあるが、私自身は戻り値で知らせる派であった。

ただ、例えば引数がNULLだったり範囲外だったりしたら、お仕事ではチェックしてエラーを返したりするけど、自分でやるときはassert()で落としたりしている。
これがJavaだと、NullPointerExceptionとかIllegalArgumentExceptionがあるので、それを返すだけになっている。
assert()だとNDEBUGで無効になるし、なによりアプリが落ちる以外の選択肢しかないのが厳しい。
「ダメなんだけど、何も死ぬことはない」という状態だ。


この辺については、自分の中で整理をしないといかんですな。