0

AndroidAnnotationsで非同期処理を1つずつ実行する

AndroidAnnotationsを使い始めた。
findViewByIdを@ViewById(R.id.button)とか書けたり、@Click(R.id.button)の後に関数を定義でクリックリスナーとして登録されたりとかで便利だった。

1つずつ実行したい


@Backgroundを関数の頭に付けるとその関数は非同期実行されるようになる。AsyncTaskなどを自分で書かなくてすむ。
でも複数の非同期な関数を順番に呼び出したい時があって(例えばWebAPIとか)、一気に実行されたら困る。
順番に実行するには1つ目の関数のコールバックに2つ目の関数を登録するか、Promiseみたいなものを返すとかしなければならなくなる。

なので非同期処理を順番に実行したい時はRxJavaとかjdeferredとか使わないといけないのかな・・とか思ってたけどAndroidAnnotationでも綺麗に書けた。

@Background(serial = “id”)


WorkingWithThreads · excilys/androidannotations Wiki

@Backgroundは何も指定しないと全て並列に動作するのだが、同じserialを指定されたBackgroundは1つずつ実行される。Threadを管理しているBackgroundExecutorがqueueを持っているらしい。

おかげで例えば
(Background) Twitterのscreen name取得
(UiThread) screen nameを画面に表示
(Background) screen nameを使ってアイコン画像取得
(UiThread) アイコンを表示
のような処理を簡単に書ける。

@Backgroundや@UiThreadを付けた関数は値を返す事ができないので、classのメンバ変数を値渡しに使うと

    String mSreenName;

@Click(R.id.buttonShowTwitterInfo)
void showTwitterInfo(){
getTwitterScreenNameAsync(); // 1つずつ実行される
getTwitterProfileImageAsync();
}

@Background(serial = "twitter")
void getTwitterScreenNameAsync(){
mScreenName = mTwitter.getScreenName();
displayTwitterScreenName(mScreenName);
}

@UiThread
void displayTwitterScreenName(String name){
mTextViewScreenName.setText("@" + name);
}

@Background(serial = "twitter")
void getTwitterProfileImageAsync() {
User user = mTwitter.showUser(mScreenName);
URL imageUrl = new URL(user.getBiggerProfileImageURL());
Bitmap profileImage = BitmapFactory.decodeStream(imageUrl.openConnection().getInputStream());
displayTwitterProfileImage(profileImage);
}

@UiThread
void displayTwitterProfileImage(Bitmap image){
mImageViewProfile.setImageBitmap(image);
}

まあなんとか我慢できる程度に読みやすいコードになる。やっぱりPromiseみたいに値をつないで渡していけた方がいいけど

0

slackのreactionをhubotで通知する

slackでは個別の発言に絵文字でリアクションを送る事ができる。

これをhubotに組み込むと
https://gist.github.com/shokai/d23607d91ea7885a8df7

configに書いてあるchannelにリアクションを通知してくれるようになる

自分が見ていないchannelに関しても、リアクションがあった == だいたいのダイジェストが見れるようになるので便利。


元々はstarを付けた物を通知していたんだけど、
slackでふぁぼったのをhubotで通知する
star_addedイベントが来なくなってreaction_addedが来るようになったので書き直した。
star_addedの頃は毎回本文が通知されていたけど、たぶんそれはreadが発生するから重かったんじゃないかと思う。reaction_addedは投稿のIDしか来ない。

0

BlendMicro npmが自動再接続・複数接続できるようになった

BLE接続のArduinoマイコンボードBlendMicroと簡単に通信できるnodeライブラリをアップデートした。

https://www.npmjs.com/package/blendmicro
v0.2.3になった。

普通BLEで通信しようとすると、PeripheralをScanしてServiceを探してTXとRXを探して……とかなり面倒な実装をしなければならないのだが、このライブラリを使うとマイコン側のble_set_name関数で指定した名前で探して接続まで全部やってくれるのでとても簡単に通信ができる。


自動再接続

BlendMicroと距離が離れて電波が通らなくなったり、太陽電池で動かしていて電源が落ちたりした時に、自動的に再scanしてちゃんと再接続できるようになった。
この機能はデフォルトで有効になっている。

というかもともと再接続は実装していたんだけどバグがあったのでちゃんと直した。3日ぐらい動かしっぱなしにしていて調子が良かったのでリリースした。

blendmicro.reconnect = false;
を指定すると無効にできる。

複数のBlendMicroへの同時接続


今までは1つのNodeプロセスから1つのBlendMicroにしか接続できなかったんだけど、うまくイベント管理したら複数同時接続できるようになった。

これはblendmicro npmを内部で使っているble-firmataでも使えるので、例えばこういう風に複数のBlendMicroに接続してそれぞれアナログ入出力させたりが簡単に書ける。

var BLEFirmata = require('ble-firmata');

var devices = [ "BlendMicro", "device2", "device3" ];

devices.map(function(device_name){
return new BLEFirmata().connect(device_name);
}).forEach(function(arduino){

arduino.on('connect', function(){
console.log("connect!!" + arduino.peripheralName);
console.log("board version: "+arduino.boardVersion);
});

arduino.once('connect', function(){
setInterval(function(){
var an = Math.random()*255;
arduino.analogWrite(13, an);
console.log(arduino.peripheralName + " -> " + arduino.analogRead(0));
}, 100);
});

});

もちろん複数台でも切断時再接続される。

BLENanoと通信できるようになった

BlendMicroと同じRedBearLabが作っているBLENanoという、もうひと回り小さいボードと通信できた。

俺はBLENanoを持っていないんだけど、hiraku君がBLENanoのサンプルコードの一つであるSimpleChatを書き込んだBLENanoを持っていてそれと通信させてもらったら通った。
SimpleChatの上の方に書いてあるService/TX/RXのUUIDがBlendMicroと同じなので、特に何も変更せずに動くことが確認できた。

なおBLENanoが手元にないのでble-firmataのBLENano版ファームウェアが書けないのだが、たぶんBLEFirmataSketch.inoを少し修正すれば動くと思うので誰か作って欲しい

add BLENano Firmata firmware · Issue #6 · shokai/node-ble-firmata
add BLENano sample code · Issue #7 · shokai/blendmicro-node

0

coffeeからES6(babel)に少しずつ書き換える

coffee-scriptで書いていたwebアプリをES6(babel)に書きなおした。

全部を一気に書き直してハイ動いたーとやるのは無理なので、coffeeとES6のファイルが混在しても動かせるようにして、少しずつ書き直した。


書き直したのはこれ
React+Fluxxor+socket.ioでfluxなチャットを作った
https://github.com/shokai/node-flux-boilerplate

インストール

% npm install babel babelify browserify watchify -save-dev
とやっていたのだが、数日前にbabelが5から6にアップデートされて、babel-coreやbabel-preset-*など色々分割されて、大分色々変わってしまった。周辺ツールも対応の過渡期だったのでbabel5系を使うようにバージョンを指定した。

  "devDependencies": {
"babel": "~5",
"babelify": "~6",
"browserify": "~11",
"watchify": "~3.5"
}


クライアント側


coffeeで書いたReactのプログラムをbrowserifyでjsに変換して1ファイルに固めていたので、単純にtransformを複数指定してやったらES6とcoffeeを混在しても大丈夫になった。

% browserify --debug --extension=.cjsx --extension=.coffee --extension=.es6 -t babelify -t coffee-reactify client/app.jsx -o public/js/bundle.js

coffeeもES6もまとめてES5になってbundle.jsができる
もちろんwatchifyでも同様にES6/coffee混在可能。

babel6だったらたぶんこれで混在できる。browserifyやbabelifyも最新に上げてから
% browserify --debug --extension=.cjsx --extension=.coffee --extension=.es6 -t [ babelify --presets [ react es2015 ] ] -t coffee-reactify client/app.jsx -o public/js/bundle.js


サーバー側

サーバー側はregisterを使う。registerはrequire関数をhookして実行前に翻訳してくれるしくみなので、例えばbabel/registerを使うとcoffeeからES6をrequireして実行できる。

Require Hook · Babel
register.coffee

最初に起動するファイルがserver.coffeeの場合

こうやってアプリを起動している場合
% coffee server.coffee # 起動

babel/registerで動的にES6を実行できるようにする。server.coffeeのなるべく上の方で

require "babel/register"

を書いておくと、これで以後coffee内からES6をrequireするとbabelが逐次変換してくれるので、混ぜて書ける。
require "models/user" # user.coffeeが読み込まれる
require "controller/main.es6" # main.es6が読み込まれる

babel 6.x系だと別のnpmに切り出されて、babel-core/registerに変わった。


最初に起動するファイルがserver.es6の場合


最初に呼び出すファイルをES6で書きなおしたら、
% babel-node server.es6 # 起動

coffee-script/registerで動的にcoffeeを実行する。server.es6のなるべく上の方で
require("coffee-script/register");

以後ES6なスクリプトからcoffeeをimport/requireすると逐次変換して実行される。

同じファイル名の.coffeeと.es6があったらどうするか

拡張子まで書けば指定できる。
require("foo.es6");

ES6に統一し終わったら.coffeeファイルを消して、requireの拡張子も消せばok
require("foo");


npmの場合

coffeeで書いてES5にトランスパイルしてnpmjs.orgにリリースしているプロジェクトを部分的にES6で書き直す場合は、試してないけどたぶんビルドプロセスを
coffeeの変換の後にbabelの変換、という順に実行してbabel側のファイルで上書きしてやるようにすればいいのではないかと思う。

0

MacとAndroidでUSBテザリング

最近Nexus5でWiFiテザリングしてると妙にMacbook側のインターネットが重くて、たまに全く速度が出ないどころかHTTPリクエストがタイムアウトする事さえあった。

WiFiをやめてUSBテザリングしたら明らかに速くなった。街はWiFiが飛び交っていて無線通信できないらしい。

USBテザリング

AndroidとMacをUSBケーブルで接続して、Androidの
[設定]→[もっと見る]→[テザリングとポータブルアクセス]→[USBテザリング]
を有効

これだけではMac側にネットワークアダプタとして認識されなかったので、

HoRNDISをMacにインストールして再起動
HoRNDIS: USB tethering driver for Mac OS X | Joshua Wise's domain

無事認識された

Windowsだと何も設定せずにネットワークアダプタとして認識するらしい。
そういえばauのAndroid使ってた時も別のau専用のUSBテザリング用ソフトをMacにインストールした気がする。