Infinito Nirone 7

Androidとか自転車とか

あけましておめでとうございます

今年も例によって浅草寺へおみくじを引きに行ってきました。

f:id:KeithYokoma:20180102145418j:plain

何一ついいこと書いてないのに凶じゃないのかよ!!

2017年振り返り

はやりのビッグウェーブ*1に乗って2017年を振り返りたいと思います。 ちなみに、1月のツイートを遡るだけでも随分な数になったし、何を思ってそれを言ったのかよく覚えていないものもたくさんあってちょっと後悔しています。

1月

あけおめを言うでもなくシステムUI調整ツールでうるう秒がどうなるかチェックするところから始まった2017年でした。今年も一年良くも悪くも Android にどっぷり浸かっていました。 年始は初日の出を見に多摩湖まで行って、ちょっとギリギリの時間だったので既に多くの人が詰めかけていましたが間に合いました。帰ってきて、うるう秒を確認して、なんやかんやして飛行機で名古屋に飛ぶということをしました。

毎年やんやかんや三が日のうちに浅草寺へ行くんです。目的は大凶のおみくじ。いつだったか知らずにおみくじをひいて大凶をあてて以来、年始にどんな大凶を引き当てることができるかでワクワクしているのですが、今年は吉でした。でも待ち人は来なかった。

はい。

年始に一度ためした Alacritty ですが、結局最近になるまで放ったらかしにしてしまっていて、久しぶりに触ったら良い感じに動いているので常用することにしました。ただし Terminal.app みたいなタブができないのでなんとかしないといけないんですが、今まで gnu-screen で満足していたものの久しぶりに入れようとしたら完全に綺麗さっぱり gnu-screen のことを忘却していたので、これを期に tmux デビューしました。とりあえず Alacritty で直接日本語入力できない・ターミナル上の文字をCommand+Cできない以外は満足しています。

はいコンテキスト過ぎるつぶやきですが、いきいきフレッシュちんちん豆という商品が実在するという話です。

あと1月はRobolectricとMockitoでウンウンうなっていたようです。

2月

DroidKaigi 直前の季節感ですね。2017年も良い感じにコントリビュートできて幸せです。

これは声を大にして言いたいと思ったけど今年は歯磨き粉みたいなチョコミントのお菓子が発売されていてこれはないなと思いました。でもだからといってチョコミントは歯磨き粉ではありません!

たしかこの日はじめて自由が丘の Bianchi Cafe and Cycles に行った気がする。

今年は夏前に50kg台に減らして、今もとに戻ってしまったので完全に進捗ダメです。

DroidKaigi 2017 では WindowManager の話をして、そのあと Oreo で「WindowManager シュッと整理しました」と発表がありましたね😇。

今年は筋肉食堂にも何度か足を運びました。アサイーボウルプロテインに浸かっていておいしい。

3月

オフィスが今の場所に引っ越す前は表参道のまい泉に近くて毎週ランチ行ってました。また行きたい。

自転車レース(ヒルクライム)に出る実績を解除しました。富士山、美ヶ原、嬬恋村と3回、とてもいいヒルクライムになりました。

はい。

次の DroidKaigi もたいがいニッチな話をするのですが、2017 の DroidKaigi も割りとニッチだと思っていたところ結構聞きに来てくださる人がおおくてびっくりしました。

叶ってよかった。

このビールたちを全部飲み切るのに半年以上かかったようです。

シリーズAの発表が3月だったの、もう随分と昔のように感じています。

4月

会社で花見をしようとしたところ残念なことに雨がふってきたのでオフィス内のディスプレイに桜の写真を表示して疑似花見をしました。

このあと見事に忘れました。

超うまかったのでまた行きましょう。

年始頃は完全無欠コーヒーやってたんですが、味が単調で飽きるのと、なによりココナッツオイルでお腹の調子が制御不能になってバラムツ食べすぎたみたいになるのがしんどすぎるのでやめてしまったし、やめてもダイエットは出来ていたのでまあ自分でちゃんとセルフコントロールしながらコンスタントに体重落としていけばいいよねって思いました。おかげで使い切れなかったココナッツオイルが冷蔵庫に眠っています。

つくばまで往復チャリライド、行きの道を少し間違えてなかなかしんどかったです。国道4号を北上して久喜までいき、そこから東に進路を変えましたが茨城県に入ってからつくば市にいたるまでが結構長かったのをよく覚えています。日が沈む前に帰ってこれてよかった。

本人からいいねがついているの今気がついた。

Oisix さんとこでもくもくした時ナマの野菜をぼりぼりしたの最高でした。

5月

結局今年は何度か肉料理のそれがしに行きました。

子の権現、このときは北側から登ったのでこの坂は降りるだけでしたが、乗ったまま降りるのはとてもおっかないとおもったのでホテホテ歩きました。次はこちらから登っていくぞ…

都民の森へグループライドしたのはいい思い出。

これの松郷峠まではやったんですよね。キツかったです。

そういえばこんな峠三昧なコースも走りましたね。

今年一恥ずかしかったやつだ。

結局 Kotlin 成分は 20% 弱まで増えました。

6月

はい。

はい。

はい。

頑張りました。来年はもっと頑張ります。目指せブロンズ!

美味しかったのは覚えているがどこだったかはもう覚えていない。

これここ最近毎年なっていて、疲れが溜まっているとか、太り気味だとか、いろんな要因があるらしいですが自分の場合は毎年春に周期的になるのです。体調を整えればそのうち治るのですが、耳鳴りと言うとあまり良いものではないのは事実なので、もしなにか急に耳鳴りがすると感じたら何を置いてでもすぐに病院へ行きましょう。

富士ヒルの翌週は家族旅行ということで大分へ。

帰りの大分空港でご飯。 このあと羽田への着陸で高度を下げ始めるあたりからずっと飛行機が揺れっぱなしで気持ち悪くなって早く終わってくれ頼む!!と思ってたら着陸やり直しになってフラフラになりました。

ミドルグレードの端末としてはよく出来ている、限りなくバニラAndroidに近い端末です。なかなか良いんですが、地味に大きいのが玉に瑕というところでしょうか。

ツールド美ヶ原の前日に行ったここのとんかつ屋さん、めちゃうまでした。

早朝の雨も上がって良い感じに大会が開催されました。序盤のいちばんきつい登りで、グレーチングの上を通ったときに後輪がズルリと滑ったときはちょっと焦りましたが転ぶこと無く登りきりました。

7月

伊豆に行きました。

逗子に行きました。

茅野から松本までビーナスラインを走りました。

翌月の中旬までアメリカに滞在しました。

アメリカの山も厳しかった。

8月

結局帰国まで治らずじまい。

サンノゼにいると日本食に困らないんですが、世界の山ちゃんまで来るとは思っていませんでした。

これ、今年一おすすめの動画です。

SFO からも近いし、カルトレインの駅からも歩いて行ける距離なので最高。

日本でも売ってくれないかな…

カリフォルニアのパレスサイクリングって感じですが適度にアップダウンもあるし観光地もあるので最高です。ただし風が強め。

アメリカ出張は実は急な話だったので、父親が「機会があるならアメリカで日食を見てくるといい」と言う話を聞いていたこともすっかり忘れていたのですが、良い感じに日食を観測できました。 でもそのあとの Android O の発表が肩透かし感あってびっくり…

アメリカの家の構造について知見を得ました。でもテレビ貴様は許さん。

尊い

ダメです。

9月

結局最終的に4000円くらいにまでは膨れ上がりました。 何もしてないけど資産が増えるってすごいですね!

これすごくいいんです。さわり心地もソフトでかつグリップ感があります。

今年一頑張ったヒルクライム。しかしキツかったな。 来年はこの先にある渋峠にも行きたい。

昨今の事件で話題に事欠かない相撲界隈ですが、両国国技館のエンターテインメント感はすごいなと思いました。

Essential Phone はいいぞ。 Andy Rubin 氏にもいろいろよくわからない噂がつきまとっているようですが、彼は退職ではなく休職で既に復帰しているということはここに残しておこうと思います(よくEssentialからAndyおらんくなったという話をきくので)。

ツールド東北は台風の影響でコースがカットされましたが楽しめました。そして AirBnB のホストのおばちゃんがすごいよくしてくれた。

これすごい分かるんですよ。自分の場合は自転車ですが。

最近はウェッブサービスでアニメもテレビ番組も映画も配信されている時代なので、あえて BD を買う理由も殆ど無いように思うのですが、しかし「この世界の片隅に」と「聲の形」はどうしても形あるものとして持っていたくて買いました。

10月

アメリカからデザイナーの同僚が出張できているときにそういう話になったのですが、みんな考えてたことは同じなんだなぁと。

DevFest Tokyo での発表は Android の進化の系譜ということで、API がどうかわってきたかを振り返りました。資料が意外と難産でした…

いつも米心は最高である。

今年一やばいGIF。

ジャパンカップを初観戦。雨降ってましたが見れてよかった〜。

ミーハー丸出しですが楽しかったので良し!

ジャパンカップ翌日はLottoNL Jumboチームのアフターパーティーに参加しました。後日 cyclowired の記事に自分が写ってる写真が載っているのを見つけました。

www.cyclowired.jp

埼玉のラルプデュエズと名高い根性坂に敗北。

いまでもつい懐かしくなって遊んでしまう。。

11月

海浜幕張までチャリで行ったのは端的に言って間違っていた…でも帰りにいい景色が見れたのでよし。

尊い。自転車と組み合わせたら無限にいろんな景色が撮れるし、ちょうど紅葉の季節で捗りました。

ツール・ド・フランスさいたまクリテリウムも見に行きました。今年は会場でいろいろ楽しめた!

クリテリウム後にいわたんちでB級映画見ました。

金色に輝くステータスになりました!

今から楽しみ過ぎる…

尊い

尊い

米心アゲイン。尊い

そういえばまだここに行けていないので来年行きたい。

12月

はー行けてよかった。

目白はのぞき坂。ここからほぼ毎日自転車通勤の日は帰りにここを通っています。 Strava で記録も見れてよい。

最の高。

尊い

尊い

ガンダムのことはわからないけど今年二度目のガンダム見学。

2017年も Proguard に消耗しました。

まとめ

来年もよろしくお願いします。

RxEither を RxJava2 に対応してみた

もともとのモチベーションとして、Java で Either を扱いたくて色々探していました。 そのなかで RxJava との運用もカバーしてくれている RxEither を見つけたのですが、あいにく RxJava 1.x で止まっていたので RxJava 2.x に対応させてみることにしました。

github.com

github.com

基本的にはパッケージの変更と、Action1 とか Func1 とかを Consumer やら Function やら Predicate やらに書き換えていく作業をポチポチとやっていきます。 テストもあるので、そちらもパッケージ変更とクラスの変更をやります。あとはテストを実行してオールグリーンになるのを見届けました。

一つ気がかりというか困ったのは、RxJava 2.x の Consumer や Function はメソッドのシグネチャthrows Exceptionとあるので、どこかで try-catch が必要になるのですが、このライブラリが依存している SealedUnion2 というライブラリでの Union2 の定義には throws Exception がないので、どうしても Left や Right で try-catch を書かないといけないというところです。握りつぶすのもあまり良くないなと思ったので今のところは RuntimeException でラップしていますが、いまいちしっくりこない…

C93 Android モダンプログラミングに RTL 対応の章を書きました

明日から C93 が始まりますね。TechBooster から Android モダンプログラミングという新刊が出ますが、そのなかにある RTL 対応の章を担当しました。

techbooster.github.io

この章を見れば RTL 対応に必要なことが大体網羅できるはずです。よくあるレイアウトの話以外にも RTL 対応が必要なことはたくさんあるので、その辺をシュッとイイカンジにやるための内容をコンパクトにまとめました。

で、一つこの章には載せきれなかった話があります。レビューをしてもらっているときに「ViewPager ってどうなの」という話が出てきて、そういえばヤツは RTL だからといって向きが逆になったりしないな?と思ってちょっと調べてみました。

結論というか、普通にしてても ViewPager は RTL だろうとなんだろうと左から右にページを追加していくので、何かしらのトリックが必要です。いくつか方法があります。

一つ目は、ViewPager を rotationY で 180度まわしたあと、それぞれのページをさらに rotationY で 180度まわしたら向きを逆にできるのでこれで RTL 対応できる!というものすごくアクロバティックな Workaround。いろいろ犠牲にしているものがありそうな気がしますが、PagerTabStrip を一緒に使っているとそれの様子がおかしくなるようです。

mobikul.com

二つ目は、RTL のときと LTR のときで Adapter でもつコレクションの中身を逆順にする方法。最初に表示する位置を LTR のときは 0 で RTL のときは size - 1 にすれば擬似的に RTL 対応できます。単純でわかりやすい解決方法です。

stackoverflow.com

この他、サードパーティ製の RTL に対応した ViewPager ライブラリがいくつか存在します。ViewPager のロジックに RTL へ対応するためのコードを書き足す一番素直な解決方法です。

github.com

github.com

github.com

何にしても頑張りが必要ですね。一番ラクなのは二つ目の"Adapter でもつコレクションの中身を逆順にする"方法だと思います。

諸君、私はAndroidが好きだ

諸君、私は戦争が好きだ: wids.net

作ってみました。

諸君、私はAndroidが好きだ
諸君、私はAndroidが好きだ
諸君、私はAndroidが大好きだ

クラッシュが好きだ
ANRが好きだ
機種依存問題が好きだ
激安端末が好きだ
売れないタブレットが好きだ

アメリカで
日本で
中国で
インドで
ヨーロッパで

この地上に存在するありとあらゆるAndroidが大好きだ

機種依存問題でクラッシュするときが好きだ
OSバージョンが上がると別の問題が発生するときなど心がおどる

フレームワークをハックすることが好きだ
リフレクションとIPCを駆使して便利機能を実現したときなど胸がすくような気持ちだった

OSのカスタマイズが好きだ
iOS と寸分違わぬ見た目になったときなど感動すらおぼえる

とても小さな筐体の端末で Android が動いているときなどもうたまらない
Google I/O で新しいバージョンが発表されるのは最高だ

iPhone 端末を落として画面を破壊したのを
見た時など絶頂すら覚える

ハードウェアのカスタマイズが好きだ
SDカードの書き込み先が内部ストレージのときはとてもとても悲しいものだ

多様性のある端末の筐体が好きだ
Android なんてダサいよねと言われるのは屈辱の極みだ

諸君 私はAndroidを 重戦車様なAndroidを望んでいる
諸君 私に付き従うAndroid好きの諸君 君たちは一体何を望んでいる?
更なるAndroidを望むか 
糞の様なAndroidを望むか?
核シェルターのようなAndroidを望むか?


Android!! Android!! Android!!


よろしい ならばAndroidだ

だが、Galaxy端末で無限に設定が保存されないバグに耐え続けて来た我々には
ただのAndroidではもはや足りない!!
大Androidを!! 一心不乱の大Androidを!!

我々はわずかに小数
iPhoneユーザーに比べれば物の数ではない
だが諸君は一騎当千のAndroidユーザーだと私は信じている
ならば我らは諸君と私で総兵力100万と1人の幾多のクラッシュを超えて無敗の集団となる
我らを忘却の彼方へと追いやり、iPhoneユーザーを叩きのめそう
髪の毛をつかんで引きずり下ろし 眼(まなこ)をあけて思い出させよう

連中にAutoLayoutの苦行を思い出させてやる
連中にXCodeの苦行を思い出させてやる
Androidには奴らの哲学では思いもよらないGoogleがある事を思い出させてやる
1000人のAndroidユーザーの集団で 世界をAndroidで埋め尽くしてやる

目標 Apple

Pixel作戦 状況を開始せよ

征くぞ 諸君

Canon EOS 6D MarkII を持って散歩した

前まで使っていた EOS Kiss X3 が天寿を全うしまして、新しいのほしいなと思っていたときに神楽坂つむりさんのブログ記事を読んでフルサイズよさそうだな?と思っていたところ、フルサイズはいいぞという声をTwitterでも頂いたので、自転車もあることだしあちこち行った先でパシャパシャ写真でも!ということでいろいろ撮ってみました。写真のウデマエは……構図の基本が知識としてあるくらいなのでウマいわけではないんですが、色んな人の写真を見ているとなにで撮ってても「この構図いい!」と思ったら即パク真似してみたりしています。来年はもっとカメラと一緒に出かけたいなぁ。ちなみに新しいカメラはEOS 6D MarkIIで今年一番高い買い物でした。たぶん。結果フルサイズはいいぞってことで、買ってから既に2ヶ月くらいたってますがあちこちで撮りまくってます。

f:id:KeithYokoma:20171105123249j:plain

f:id:KeithYokoma:20171105124956j:plain

f:id:KeithYokoma:20171105125352j:plain

f:id:KeithYokoma:20171105125403j:plain

f:id:KeithYokoma:20171105153920j:plain

f:id:KeithYokoma:20171105154317j:plain

f:id:KeithYokoma:20171105154434j:plain

f:id:KeithYokoma:20171105154451j:plain

f:id:KeithYokoma:20171106230925j:plain

f:id:KeithYokoma:20171111130529j:plain

f:id:KeithYokoma:20171111180813j:plain

f:id:KeithYokoma:20171112152543j:plain

f:id:KeithYokoma:20171112152643j:plain

f:id:KeithYokoma:20171218134844j:plain

f:id:KeithYokoma:20171218132403j:plain

f:id:KeithYokoma:20171209142001j:plain

f:id:KeithYokoma:20171209140651j:plain

f:id:KeithYokoma:20171210130839j:plain

フルサイズはいいぞ。

レンズは標準のやつを使っています。EOS Kiss のときのものは使いまわせないのでどうにかしよう。フルサイズともなるとレンズ沼にはまったらすごい勢いでお金が溶けていくのが目に見えているので、レンタルとかで楽しむのもアリかなと思っています。一応パンケーキレンズもあるし、困るのはすごい遠い被写体に寄りたいときだけ。でも知ってるんだ、望遠レンズは暗くなりがちでその中でも明るいレンズを選ぼうとするとカメラがもう一台買えるくらいすることを……やはりレンタルだ。

SparseArray から要素を取り出したときに ClassCastException が発生するパターン

SparseArrayAndroidフレームワークにあるコレクションの一種で、Integer を key にした HashMap よりもメモリ効率がよいとされるコレクションです。

SparseArray には 2 通りの値を取り出すメソッドがあります。一つはSparseArray#get(int)もう一つはSparseArray#valueAt(int)です。 どちらのメソッドも同じint型の引数をとりますが、getメソッドの引数はkeyで渡された値をもとにバイナリサーチをかけて内部の配列のindexを決めており、valuesAtメソッドの引数はindexで値がそのまま内部の配列のindexとして扱われます。

SparseArray はまた要素を追加した後に削除することもできます。こちらも 2 通りのメソッドがあり、それぞれSparseArray#delete(int) / SparseArray#remove(int)SparseArray#removeAt(int)で、delete(int) / remove(int)の引数はkeyでこれをもとにバイナリサーチをしてアクセスすべきindexを決め、removeAt(int)の引数はindexでそのまま内部の配列のインデックスとなります。

他にも同じパターンで引数のintkeyなのかindexなのかで挙動の異なるメソッドがあります。

さてここで、一度 SparseArray に保存した値を削除し、再度取り出すことを試してみます。

SparseArray#put(int, V)で指定したkeyに保存したのち、SparseArray#remove(int)で指定したkeyに対応する値を削除、SparseArray#valueAt(int)で先頭の要素を取り出します。

val array: SparseArray<String> = SparseArray()
array.put(0, "hoge")
Log.d("SparseArray", "Value at [0] == ${array.valueAt(0)}")
array.remove(0)
Log.d("SparseArray", "Value at [0] == ${array.valueAt(0)}")

同じようなことをSparseArray#get(int)で実行する場合は次の通りで、get メソッドに渡すkeykeyAt(int)で取り出します。

val array: SparseArray<String> = SparseArray()
array.put(0, "hoge")
Log.d("SparseArray", "Value at [0] == ${array.get(array.keyAt(0))}")
array.remove(0)
Log.d("SparseArray", "Value at [0] == ${array.get(array.keyAt(0))}")

それぞれどのような結果になるかというと、SparseArray#valueAt(int)の場合は最後の行でClassCastExceptionが発生してクラッシュし、SparseArray#get(int)の場合は最後の行でログに"Value at [0] == null"と出力されます。

SparseArray では要素の削除を実行すると、内部で保持している配列の該当箇所に削除したことを示す DELETED という Object 型の定数を代入します。SparseArray は型パラメータでどの型のオブジェクトが保存されるか指定できますが、実際には内部で要素を保持している配列は Object[] です。そして SparseArray#get(int) はその場所にある要素が DELETED なら null ないしは指定した値を返すようになっていますが、SparseArray#valueAt(int)は特にそのようなチェックなしに指定した場所にある要素を返しています。これがSparseArray#valueAt(int)を使ったときにClassCastExceptionが投げられる理由です。

要素が全部なくなったのに要素にアクセスしようとするというのはよくない状況です。get(int)keyAt(int)を組み合わせて null チェックをすることでClassCastExceptionは回避できますが、根本的に並行処理に問題がある(SparseArray はスレッドセーフではない)ということなので、クラッシュレポート等で身に覚えのない ClassCastException がある時にはこのパターンを疑ってみると良いと思います。