2016年6月1日

[android]2つspinnerを使ってみる (4) - トーストを出す

今までLog.d()でログしか出していないので気付かなかったが、これではToastすら表示させることができない。

Contextがあればよいようなのだが、今動いているアプリのContext取得、みたいなものがわからなかったので、ボタンを押したときに引数でActivityを渡すことにした。

hirokuma/AndroidTwoSpinners at d9bb9bb266a9b4203006c842927a7348cb72c572

 

Activityも、ボタンを押したときのハンドラの中では取ってこれそうな気がしないので、onCreate()のときにthisをprivate変数に保存するようにした。
ButtonはContextを持たないから、android.app.Activityが手っ取り早いだろう。

あ、ここを見ると、同じclass内だからだろうけど「どのthisか」を指定するようにすれば、Activityのthisをprivate変数で保持するようなことはしなくてよいらしい。
【AndroidStudio】ボタンをクリックしてToastを表示する方法 - Qiita

 

動いているからこれでいいことにしているが、いろいろとやり方がありそうな気がして安心ならん。
こういうのが「ちょっとアプリは・・・」と思ってしまうところだな。

 

いかんいかん。
否定する心で物事を眺めていると、無意識のうちにそういう方向に判断してしまうかもしれないから、日頃から心を広くしておかねば。

[android]2つspinnerを使ってみる (3) - テスト用classを別にする

もはやspinnerとは関係なくなったが、気にすまい。

テストが増えていくと、ファイルの行数が増えていくので、テスト用のclassを別にしてみた。


hirokuma/AndroidTwoSpinners at af43513ee3a892089bac0e81afcac8a403b4f85c

Test1.java、みたいなものを作って、MainActivity.javaにメインカテゴリーとサブカテゴリーを追加すればよいはず。

 

メインカテゴリーなんて名前だけだから、

class AAA {
  static final String MAIN_NAME = "NAME";
  static String getName() { return new XXX { MAIN_NAME }; }
};

class BBB extends AAA {
  static final String MAIN_NAME = "BBB";
};

みたいな感じにできないだろうか、と期待したのだが、ダメだった。
staticの考え方は、C++と同じでよさそう。

 

じゃあ、interfaceにしてgetName()だけ定義しておくか、とも思ったが、今回のように各classのメンバを呼び出すだけだったら、継承関係にしておく意味がない。
ばらばらにしておくか、というところで終わったのが、今回コミットしたものになる。

2016年5月31日

[android]AsyncTaskとキャンセル

私が持っているAndroidの本には載っていないのだが、今は非同期で動かすというか、ワーカースレッドみたいにして動かしやすいようにAsyncTaskなるものがあるそうだ。

 

使い方はそこまで難しくなく、ネットに出てくるサンプル通りに書けば動いた。
しばらく非同期部分を動かしたかったので無限ループにすると、通らないルートが発生するためか怒られた。
適当にif文とbreakを追加すると言われなくなったのだが、そんな対応でよいのだろうか。。。

 

無限ループも、ぐるぐる回すと忙しすぎるので、Thread.sleep()で10秒ごとに回すようにした。
さて、そのときに非同期部分をキャンセルさせたかったら、どうするとよいのだろうか?

 

最初に試したのは、フラグだった。
停止フラグを用意して、ボタンを押したらフラグを立てる。
無限ループ内では停止フラグを見るようにして、立っていたらbreakで抜ける。
まあ、動くには動くのだが、sleepしている間は待っておくしかないので、ちょっとわびしい。

どうやら、Thread.sleep()は、Thread.interrupted()を呼ぶと抜けるらしい。
シグナルみたいなものか。

 

そうしていくうちに、isCancelled()があることに気付いた。
AsyncTaskを継承するとcancel()が追加され、これを呼んでやるとisCancelled()が立つので、無限ループでもこれでbreakすればよいようだ。

cancel()を呼ぶと、onCancelled()が呼び出されるようになっている。
じゃあ、さっきのThread.interrupted()をここで呼んでやるようにしておこう。

が、そこまでしなくても、cancel(true)としてやるとThread.interrupted()が呼ばれるようになっているみたいだ。
「mayInterruptIfRunning」では、わからんかったなぁ。
"thread"はAsyncTaskのことを指しているのかと思っていたよ。。


DDMS...じゃなくて、Android Device Monitorでアプリのスレッドを見ると、AsyncTaskをnewしていくとこういう風に見えた。

image

doInBackground()を抜けても数は変わらなかったが、StatusがWaitになった。
ArrayList<>に保持していたものもclear()しているから、消えると思ったんだけどね。
まだGCのタイミングじゃないと言うことか。
あるいは、1つずつ消さないといけないのか。。。
  • ArrayList#clear()だけ
    • 残ったまま
  • さらに=nullもする
    • 残ったまま
Android Device Monitorから「Cause GC」としても残っているので、消える対象になっていないのだろうという気がする。
うーん、この辺りをどうしてよいのか思いつかないところが哀しいですな。

[esp8266]NFCIDを取得してTCP送信

ESP8266にRC-S620/Sをつなぎ、ポーリングしてNFCIDを取得し、TCPのサーバに送信するというだけのアプリを作った。

hirokuma/esp8266_rcs620s at 32464d397720708f8a45274fd52e9671eb8cbe40

 

何に使うのか?といわれると、さあ?、としか答えられないアプリだ。。。
最近どちらも動かしていなかったから、両方動かしてみました、くらいか。

TCPのサーバも、Linuxでsocketを使ってlistenしてacceptするという、教科書にあるようなものだ。
送る方がHTTP GETっぽく送って、サーバがクエリーをさばくようにすると、もうちょっとそれっぽいアプリを作ることができたのかもしれんが、まあこの辺が私の限界だ。

 

まあ、NFCIDを取得するだけだったら、このしくみは電気を食いすぎだろう。
せめてBLEか。
AndroidがあるならNFCが動くだろうから、こういうの自体いらん。
RC-S390はもっと情報を公開してくれるといいんだけどねぇ。

 

無理やり使い道を考えるとしたら、家の鍵とかか。
錠がNFC R/Wになっていて、錠とサーバがつながっている。
一瞬しか役に立たない鍵情報をサーバから送ってもらい、RC-S620/Sをカードエミュレーションさせて、情報が一致しないと開けられない、とか。。。

うん、スマホでできるね。
「無理やり」とすれば、スマホがなくてもできる、というところか。
私がスマホとか持ち歩かない人間なので、そこら辺にあるもので代用できるといいんだけどね。
「私が使うことで、それは本物になるのよ」みたいな。

2016年5月30日

[ble]Linkingのプロファイル仕様

IoTの囲い込みを打ち破るドコモの隠し玉 - 日経テクノロジーオンライン

 

Linkingは仕様書が簡単にダウンロードできるようなので、ちょっとだけ眺めた。
Project Linking

利用規約やアンケートはあるが、メールアドレスの登録すら不要だった。


構成は、こう。

  • PeripheralDeviceLink Service
    • Write Message Characteristic
    • Indicate Message Characteristic

BLEのサービスは1つ、Characteristicも2つ、しかも書込みとIndication用のみ。
なかなか割り切った作りだ。

 

紛らわしいのは、「サービス」という言葉だろう。
BLEとしてのサービスは1つしかないのだが、Linkingとしてのサービスは5つある(今のところ)。
Write Messageにはフォーマットがあり、そのフォーマットに「サービスID」と「メッセージID」がヘッダとして載るようになっている。
このサービスIDとメッセージIDの組み合わせで、何をするデータなのかが決まるというやり方のようだ。

 

まあ、デバイス側としてはCharacteristicをいくつも用意しなくてよいから、メモリが少なくて済みそうな気はする。
書込みサイズも20バイトまでと書かれているから、Long Writes対応もいらなさそう(ただし、メッセージ長が大きい場合もあり、それはシーケンス番号ヘッダを追加して対応するみたい)。

nRF5xしか知らないのだが、サービスやCharacteristicを追加したり減らしたりというのは、難しいわけではないのだが面倒な作業ではある。
なので、書き込まれたデータだけで判断すればよいのだったら、そこまでフレームワークを作ってしまって、あとはその下だけ変更するようなやり方ができそうだ。

デバイス側はBluetooth Developer Studioプロジェクトを配布してくれるとよかったのだけど、まあ、そこまでやるほどでもないか、というところだ。


中身がどうのこうのという評価はしないのだけど、ドキュメントが残念な感じだ。
Excelで作られたものをPDF化しているのだろうけど、いっそのことExcelで配布された方がありがたかった。

セル内での改行を見栄えベースで行っていると、印刷したときにはみ出てしまって、見えなくなることがあるのよねぇ。

それと、ページごとの用紙サイズがまちまちで、あまり大きくない画面でPDFビューアを使うと画面外にはみ出たりしてしまってね。。。
用紙の縦と横が混在しているわけでもないけど、どうやってるんだろう?

image


対応デバイスは、ここら辺でも売られているとのこと。
Braveridge公式ストア / Linking対応製品

安い、安いねぇ。。。
遊びで自作するのはいいけど、製品にするとなるとここまで安くできないといけないのですな。

2016年5月28日

[esp8266]UARTのRX_FIFOをアプリで読めばよい

前回の続き。

UARTの受信レジスタ(RX_FIFO)を読むuart_recvTaskというものがあるのだが、それが今のアプリタスクが終わらないと呼び出されないので、どうしようどうしよう。。。というところまでだった。

 

最初に記事を書いてから、頭の整理のためにシーケンス図を追加した。
そうすると、難しく考えすぎていることに気付いた。

もともと、uart_recvTaskもコンテキストとしてはアプリタスクと変わらない(割込みじゃないという意味で)。
なので、アプリタスクでuart_recvTaskがやるのと同じことをやってしまえばよい、と。
受信データはRX_FIFOに勝手にたまっているから、同期で読めるのだ。
なーんだ。

image

RC-S620/Sは基本的に相手から勝手にデータを送ってくることはないので、uart_recvTaskの動きを止めてしまっても良いのだが、このまま様子を見てみよう。

やればできるやん。

[esp8266]system_os_post()は割込みではない

RC-S620/Sの処理をESP8266に組み込もうとしている。
が、いきなり詰まった。

いま作っているRC-S620/S用のライブラリは、Linuxベースで作った。
同期でUART送信し、同期でUART受信する。
他のOSになったとしても、非同期を同期に見せかけるようにすればよいだろう、と思っていたのだ。

しかし、ESP8266のnon-OS版に付いているUARTドライバは、

  • UART受信は割込みで見る
  • 受信したデータをためるのはsystem_os_post()後

という形になっている。

image

RXFIFO_TOUTはタイムアウトのフラグらしいのだが、調べていない。
一定時間FIFOに変化がなくなったら立つのかな?
UART_RX_TOUT_THRHD_Sが24だが、単位がわからんな。

 

なので、UART受信タスクの優先度をアプリタスクよりも高くしておけばいいや、と軽く考えていた。
そして、アプリ側ではwhile()でUARTタスクがためた受信データを監視しよう、と。

 

これが動かない。。。
ログを埋め込んだのだが、アプリの受信待ち処理を入れなければUARTタスクで受信したデータがログ出力され、待ち処理を入れると出力されない。

たぶん、アプリのタスクによってUART受信のタスクが待ち状態になって、こうなっている。

image

 

つまり、これを動かしたければ、受信待ちは同期で行うのではなく、同じようにsystem_os_post()で回すようにしないといかんと言うことだろう。
Linux系のselect/pollみたいに待たせることができればよいのだが。

 

うーーーん・・・。
同期向けに作ってある処理を非同期化するのはとてもつらいぞ。
それが嫌なら、UARTの受信データをためる処理を割込みに持っていけばよいのだろうが、それはそれでつらい。。。

解決方法が思い浮かばん。

[esp8266]espconn_gethostbyname()への誤解

AndroidやったりLinuxやったり落ち着きがないが、今日はESP8266だ。
しかもNon-OS版なので、あまり使っている人がいないような気がする。
みんなArduino使うからなぁ。。。

 

さて、今回はespconn_gethostbyname()についてだ。
名前の通り、ホスト名からIPアドレスを引いてくれる関数なのだが、使っていてよくわからない動作をしていたのだ。
何かというと、引数にip_addr_tのアドレスを与えるのだけど、名前を引き終わってコールバックされたときにはそのアドレスは更新されず、コールバック関数の引数で渡されるのだ。
じゃあ、引数で与える意味がないではないか、という疑問だ。

 

これはEspressifのドキュメントではなく、Kolbanさんのドキュメントを読まないとわからなかった。
Espressifのドキュメントでは、この関数の戻り値がESPCONN_INPROGRESSだと「already connected」となっているのだが、Kolbanさんのを読むと「キャッシュで持っていないのでlookupしにいきます」という意味らしい。
まあ、LinuxもPROGRESS系の戻り値はそうだよな。。。

だから、戻り値がOKのときには即座に結果を引数に返すけど、INPROGRESSの場合はコールバックの引数で返す。
コールバックの際は関数呼び出し時の引数で渡したアドレスの方は使わない、という動きをしているようだ。
サンプルでは渡すipaddr_tをグローバル変数にしてあるけど、こういう動作ならばローカル変数でよいはずだ。


Arduino版だと、この辺りは見えなくなっているようだから楽そうだ。
それに、誰かの作ったライブラリを使えるのも魅力だから、そのうち移行したいものだ。

2016年5月27日

[android]2つspinnerを使ってみる (2) - 最終回

前回の続き。
スピナーを2つ使って、大項目と小項目を選択し、ボタンを押したらメソッドを呼び出す、というだけのものを作りたい。

まあ、何かというと、テストアプリだ。
大項目と小項目に分けたテスト書を作って、それに沿って実行するアプリの枠を作っておきたい。
見栄えにはこだわらないけど、ちゃんと動くことと、変更しやすいことを目的にするのだ。

 

前回までのコミットは、たぶんこちら。
スピナーのレイアウト指定が違っていたので、妙に間隔が狭くなっていたのを修正したところまで。

今回は、コマンドの実行部分まで作る。


結果として、こうした。

Cだったら、2次元配列にして、添字で大項目・小項目の番号を指定して呼び出すようにするかなぁ、と思って、そのまま作ってみた。
関数ポインタはないけれども、interfaceを使えば同じようなことができるということだったので、まねした。

 

うーん。。。
目的は達成したのだけど、Javaとしてというか、コーディングとしてどうなんだろうか?
こんな1箇所にまとめず、テストごとにファイルを分けた方が良いのかもしれない。
C#みたいに、1つのclassを複数ファイルに分けられるのだったらよいのだが。

まあ、ここはメニュー用と割り切って、テストの本体は別にした方がよいかもしれん。
テスト項目だから、これが100個も200個も並ぶと訳がわからなくなりそうだ。

 

また、今はドロップダウンにしているけど、押し間違えそうだからダイアログ形式の方がよいかもしれん。
文字列をstrings.xmlに持っていくなら、name()はStringじゃなくてintでリソースIDを返すようにすべきか。

 

なんか、悩みはじめると全然終わらんですな。
ともかく、今回はおしまい。

[android]2つspinnerを使ってみる (1)

技術紹介じゃなくて、よくわかんないなりにやってみた、というコーナーだ。
「こいつ、こんなことも知らないのー」という目線で見ていただけるとありがたい。

 

やりたいのは、Windowsの部品名で言えば、コンボボックスを2つつくって、1つ目に大項目を、2つめに小項目を選べるようにして、ボタンを押したら大項目と小項目の組み合わせに応じたメソッドを呼び出す、というもの。

Androidでは「コンボボックス」じゃなくて「スピナー」spinnerと呼ぶのですな。
スピンするイメージがないのだが、まあいいや。

 

スピナーを作って、リスナーを設定すると、選択されたときにonItemSelected()が呼ばれる。
引数にidがついているから、それで選択されたアイテムの番号がわかるようになっている。

なので、大項目のonItemSelected()のときに、小項目用のスピナーを設定すればよいはず。

 

動的にスピナーの項目を作るときは、ArrayAdapterにadd()していって、最後にスピナーにsetAdapter()すればよい、ということになっている。
実際にそれで設定できたのだが、妙にアイテム間が狭い。。。

image

小項目のArrayAdapterとSpinnerは、大項目のonItemSelected()ではcontextが合わないようなので(たぶん)、privateメンバにしてonCreate()のときに設定させている。
だからかなぁ。

 

こう、こまごま言葉で説明せずにソースを置きたいのだけど、今使っているブログエディタ(Open Live Writer)にはまだソースを載せるのに適したプラグインが出てないのだ。
GitHubに置こう。
https://github.com/hirokuma/AndroidTwoSpinners/blob/master/app/src/main/java/com/blogpost/hiro99ma/twospinners/MainActivity.java

みんなのまねして、android.R.layout.simple_spinner_itemを使っているけど、これが狭いということなのかな。

Y.A.M の 雑記帳: Android Spinnerの選択肢をXMLで指定する
ああ、スピナーの部分とドロップダウンのところで違うレイアウトになっているということか。
今回は、コンボボックスみたいなやつだから、ドロップダウンなのだろう。

image

よーし。

 

なんか、こうですね、XMLでやるところと実装でやるところがよくわかってないし、設定値の関係性もわかってないのですよ。
やっていれば、いつか見えてくるようになるのだろうか。。。