Infinito Nirone 7

白羽の矢を刺すスタイル

umeda.apk #1

umeda.apk に参加してきました。

shibuya-apk.connpass.com

shibuya.apk の関西出張版ということで、面白そうだなぁと見ていたらお誘いをいただけたので行ってみることに。 今回のテーマは Google I/O の報告ということでしたが、自分は行ってすらないので、設計にまつわるトークをしてきました。

speakerdeck.com

Bento や Burrito のメタファーは、Twitter のエンジニアであるIsrael Ferrer氏の過去の発表やポッドキャストで取り上げられています。 ちょっとまえに Qiita で Bento や Burrito に見る設計といったような、ポエミーな記事も書いたのですが、今回はもう少しまじめに、こう言う設計にするといいよという話です。

MVP とか MVC とか MVVM とか、大きな単位でのパターンはいっぱいあるんですが、往々にしてモデルはでっかく一括りで"モデル"と呼ばれてしまい、放っておくとコントローラのように太っていきがちです。 そこでこのトークでは、オブジェクト指向の原則のうちのひとつの単一責務の原則と、最近話題の Clean Architecture を取り上げ、いかに弁当のようにきれいに仕切られたモデルを作るかというところにフォーカスを置きました。

Clean Architecture で注目すべきは、いわゆるモデルと呼ばれる部分が2層に分かれている点です。厳密には、画面にも状態はあるので、その状態をもっておくプレゼンテーションのレイヤもモデルと言えばモデルですが、データをやりとりしたり、与えられたデータをもとにビジネスロジックを動かす部分は2層あります。

ひとつはリポジトリやデータレイヤと呼ばれるような部分で、非同期処理でくるんで使う部分のおおくがここに入ります。REST を叩いてデータを取り出す・変更する・作成する・削除する部分や、データベースの操作など、一番データソースに近い部分がこのデータレイヤで、作り方のパターンとしてはリポジトリパターンを使うと良いようです。 REST、データベース、メモリキャッシュなどはこのリポジトリでまとめて取り扱いますが、クラス自体は分かれます。そのため、画面に近いレイヤからみた時に扱いやすいよう、分離されたクラスをひとまとめに扱うインタフェースを用意します。最近は Rx が主流になりつつあるので、Observable を返すようなインタフェースを用意すれば便利でしょう。

リポジトリができたら、そのリポジトリとプレゼンテーションをつなぐドメインの部分を作ります。このドメインの部分がないと、リポジトリが散らかるか、プレゼンテーションが太るかになってしまう為、ドメインの部分を必ず用意します。

リポジトリはアプリケーションでシングルトン、ドメインドメインごとにシングルトンであれば良いと思います。

そういうわけで、金曜日は大阪で一泊、土曜日は半日大阪を観光して帰ってきました。また機会があれば行ってみたいですね!

正丸峠〜定峰峠

先週末はまた秩父にでかけました。所沢街道から国道299号を通り、正丸峠を超えて秩父まで。帰りはどうしようか迷いましたが、一度寄居方面に出て考えてみたところ、定峰峠という面白そうなところがあるのを見つけたのでそちらを通って帰ってきました。総行程168kmは久々の長旅でした。

正丸峠は何度も来ているところですが、やはりいつ来てもキツイですね。調子に乗ってアウターで登りきりましたが後々の足があんまりもってなかったです。 そういえば、正丸峠は心霊スポットとしても知られているようです。昼間にしか来ないので何も気にしたことはありませんが、狭い山道で鬱蒼と木々が生い茂っているので、そういうこともあるのかなという気はします。

秩父の街に降りてから、一旦羊山公園を見に行くことにしました。これがやってしまったなあという感じなのですが、西武秩父駅側から羊山公園に向かうと、小高い丘の上にある羊山公園に行くために鬼のような斜度の坂を登るはめになります。坂の長さは大したことはありませんが、一気に小高い丘を登るので斜度がきつめ。完全に足を使ってしまいましたorz

そこから国道に戻って、定峰峠へ。

定峰峠は県道をひたすらゆっくり登る道で、5km〜7kmくらいずっと登り坂が続きます。ただ斜度自体はゆるやかです。途中自転車のラックが置いてあるお茶屋さんがいくつかありました。麓の当たりにもそういうお店があって、沢山自転車が停まってたのでそこで休めばよかった……

定峰峠は自転車乗りがよく集まってくるそうで、その日も沢山の自転車乗りの人たちとすれ違いました。また、峠から分かれている道を行くと、秩父周辺にあるいろいろな峠にもたどり着けるそうで、次に来るときはそちらに向かってみたいとも思います。

最近は地図を見ると必ず峠を見るようになってきました。またいろいろ山を走りに行ってみたいですね。

青梅街道〜奥多摩湖〜都民の森〜五日市街道

久々のロングライドにでかけました。総行程140km、いつもは奥多摩湖を見たら引き返していましたが、都民の森までチャレンジしてみることにしました。

うわさに違わず、延々と続く坂がしんどかったです。半分くらいはインナーローでジワリジワリ登っていた気がします。 途中の開けたところにある駐車場で一旦休憩し、ヒルクライムを続行。下りはスピードが出過ぎないよう気をつけました。

初めてなのもあってか、本当に峠に到達するまでが地獄のように長くて、ああいう時に心が折れないようにすることと、ペースを上げすぎず下げすぎずのところをうまく維持するのって大事なんだなと思いました。 帰ってきたらクタクタになってましたが、気力を振り絞ってカレーと肉を準備して食べました。うめぇ!

いよいよ梅雨に入ってしまいましたが、引き続き折を見て運動するようにしないと……

ライブラリやフレームワークがもたらすいくつかの問題への考え方の指針

@konifar さんの Guava にまつわる以下の記事をみて、ライブラリやフレームワークへのロックイン問題を含めいろいろな問題は、気にしなくていい場合と気にした方がいい場合があるなと思ったので、その考え方の指針みたいなものをまとめようと思います。

konifar-zatsu.hatenadiary.jp

1. ライブラリ・フレームワークの大きさがもたらす問題

Android (というか広く一般に JVM) では、宣言できるメソッド数に制限があります。このため、依存するライブラリやフレームワークで宣言されたメソッド数も考慮に入れないと、アプリケーションがビルドできなくなったり、うまく動かなくなったりします。

Android においては、これに対応するものとして、Multi-Dex という仕組みが用意されています。通常、ひとつの apk にはひとつの dex ファイルを生成しますが、Multi-Dex によって、ひとつの apk に複数の dex ファイルを生成することができるようになります。これで、メソッド数の制限を回避しています。Lollipop 以降であれば OS がサポートしてくれますが、KitKat 以前ではライブラリに依るサポートが必要となります。

qiita.com

qiita.com

画期的な仕組みではありますが、一方でデメリットもあり、アプリケーションのプロセスが起動した時に、読み込むべきファイルが複数になるため、必然的に起動が重くなります。ですので、できれば使用は避けておきたいですね。 そうなると、大きなライブラリやフレームワークの導入は慎重に行うことになります。

ただ、ライブラリやフレームワークの大きさというのはただ単に、メソッド数の制限に引っかかりやすくなる、というの問題のみをはらんでいるわけではありません。

2. ライブラリ・フレームワークの解決する問題の大きさがもたらす問題

平たく言えば、抽象度が高いほど解決する問題も広く大きくなります。あるいは、先のブログ記事でも触れられている Guava のような、ユーティリティを集めたライブラリも、解決する問題がユーティリティの数だけ増えるので、大きくなりがちです。

抽象度の高さによる解決する問題の広さが招く問題は場合によりけりだと思いますが、ユーティリティを集めたようなライブラリだと、知らないと使わないものが出てきて無駄が増えがちになる、ということは言えそうです(@konifar さんのブログでもそのような場面が書かれています)。

ProGuard によって無駄になっているものを消すのもひとつの手立てではありますが、ProGuard はビルド時間に少なからず影響を出してしまうため、出来ることなら、使う分だけ入れるぐらいで留めておきたくはあります。その意味では、ユーティリティは自分たちで使いたいものだけ手元においておくようなつまみ食いをするのも良いかなと思います。

3. ライブラリ・フレームワークへのロックインがもたらす問題

抽象度の高さで問題になることで大きく取り上げられるうちのひとつに"ロックイン"があります。抽象度が高ければそれだけ応用が効くので、アプリケーションの実装のあちこちで使えるというのがフレームワークの役目ではありますが、あまりにあちこちで使ってしまうと、それ以外のフレームワークへ変えようとした時に様々な問題が表出して困る、あるいは、他のフレームワークを常用している人にとっての学習コストがついてまわる、というのがざっくりとしたロックインの問題点です。

どのようなライブラリ・フレームワークを使うかにも依存しますが、ロックインする部分をまるごと切り離せる作りになっていて、それを使いたい場所で適宜 DI コンテナなどの仕組みを用いて差し込むことができれば、気軽にライブラリやフレームワークを変更することが出来そうです。もちろん、切り離された部分の内部では、選んだライブラリ・フレームワークに合わせた実装をする必要がありますが、アプリケーション全体としてみると、一部を変えるだけで済む作りになります。

おそらく、肌感覚としては継承を前提としていたり、特定の設計パターンに合わせた作りをできるようにしてくれるライブラリ・フレームワークはこの切り離す設計が難しいように感じます。例えば、設計を支援する系のライブラリ・フレームワーク(所謂 MVP や MVVM などのパターンを簡単に実装できるようにしてくれるものたち)は、パターンに合わせた実装が必要になるため、他のパターンに乗り換えるときには、ごっそり書き換えないといけなくなります。アプリケーション全体で設計パターンをごっそり変える必要があるかどうかというのはまた別の問題として議論の余地があるかと思いますが、そうなった時に、ごっそり書き換えるコストを甘受するかどうか、という点は考えておくとよいかもしれませんね。

学習コストについては、ライブラリやフレームワークの実装を学習するよりも、もっと概念的な部分で理解をしておくほうが良い気がします。先ほどの MVP や MVVM などのパターンであれば、それぞれライブラリでどう実現しているかという部分も勿論重要ではありますが、それ以上に MVP や MVVM がどんな概念なのかという方が大事で、そこを抑えられれば、あとは実装の方法論の違いでしかなくなります。Rx の場合は方法論もそれはそれで色々ありすぎて困る部分はありますが。

まとめ

あまり"コレだッ"と言える素敵な解決法は見えてきませんでしたが、ライブラリやフレームワークを使うことで得られるメリットと、その裏にあるコストの部分を天秤にかけた時に、コストを最小化したり、デメリットを局所化するだけの用意があれば、大きいライブラリ・フレームワークもガシガシ使っていけそうだな、と言う感じはあります。たぶんここで書いたこと以外にもそういう対処法や指針は有る気がするので、こういう時はどうする・どうした・どうしたい、みたいなのが集まってくるとよさそうです。

kyobashi.dex #3 で発表してきた

当日は参加枠でお話を聞きに行くつもりでしたが、いつの間にか資料を作って発表する流れになっていたので、急ごしらえですがMediaStoreがらみのことについて LT をしてきました。

speakerdeck.com

最近MediaStoreの、特にAudio周りを触っているのですが、以前にはImagesも触ったことがあって、相変わらず面倒くさい構造になっているな……と思っていました。データの取り出し方が分かってしまえばあとは決まり文句を打つだけで良いのですが、如何せんその取り出し方が分からないものも多く、都度調べ物をしていては時間が無限にあっても足りないと感じたので、Facade のようなまとまった API をライブラリとして提供しています、というのがおおまかな概要になります。

MediaStoreそのものは単にデータベースの構成やUriを外部に公開するためのもので、その実態であるところのContentProviderは別のところにあります。開発者は通常のContentProviderへのアクセスと同じ手段でもってMediaStoreからデータを読みだすのですが、いくつかのややこしい話があります。

  1. 基本的に Javadoc が不足気味である。
  2. カラム情報を持つクラスに継承構造があり、テーブルごとのカラム情報を持つクラスは共通する親クラスのカラム情報を受け継いでいる。但し共通カラムが必ずしも各テーブルに存在するとは限らない。
  3. カラムの制約が明記されていないものがある(UNIQUE や NOT NULL など)。
  4. クエリのためのUriが正しくないと、欲しいデータが揃わない(おそらく内部的に結合を行っている)。
  5. 公開されていないUriを使って得られるデータもある。
  6. テーブルの構造的問題とContentResolverAPI の問題で、SQL インジェクションをする場面がある

最後の SQL インジェクションに関しては、4.x の時代にバンドルされていたギャラリーアプリが実践しています。ギャラリーアプリでは写真をディレクトリ単位で管理していましたが、どの写真がどのディレクトリに属しているかという情報は正規化されておらず、写真データのカラムに結合したままになっています。このため、存在するディレクトリを引いてくるには、写真データの入っているテーブルに対し、GROUP BY を伴ったクエリを発行するひつようがありますが、ContentResolverにはそのようなメソッドが生えていないため、WHERE句に相当する部分で SQL インジェクションを余儀なくされる、といった具合です。

ContentResolver#query()の引数であるselectionWHERE句を構成する部分にあたりますが、当然条件を書く上ではプレースホルダを使用します。これによって、selectionArgsに対しての SQL インジェクションは成功しないのですが、selectionそのものに SQL インジェクションをする文を書くことはできてしまいます。内部的には、selectionWHERE (%s)という文字列のテンプレートに流し込んでいるだけなので、例えば1) GROUP BY ... などとすれば、WHERE句はつねに真となって SQL インジェクションができます。

まさか公式にそれを逆手に取った実装をしているとは思いませんでしたが、まあ、そういうことです。

ContentProvider での実装如何では SQL インジェクションが容易にできる場合もあれば困難を極める場合もありますが、このあたりは(本題からはそれていますが)気をつけておくべきと言えます。まあ、Webサービスを作る場合と同じ考え方で作っていれば、このような穴が空くようには思えませんが…

それはともかくとしても、バッドノウハウとボイラプレートの山をこれ以上積み上げないためにも、また API を整理するためにも、何かしら Facade のようなものを用意して置く必要性を感じた、ということですね。

一応 GitHub には既に公開していますが、まだ mavenCentral にはリリースしていません。もうすこしやりたいことがあるので、そのめどがついたタイミングでリリースしようと思います。

GitHub - Drivemode/MediaFacade: Facade modules for dealing with complicated MediaStore.

potatotips #29 Android まとめ

potatotips の 29 回目に参加してきました。今回は初めてブログまとめ枠として参加しているので、Android の発表をまとめます。

発表内容

1. yamacraft - Multi-Window上での「共有」について

qiita.com

ウィンドウ間でのドラッグアンドドロップについてのお話でした。興味深かったのは、片方のウィンドウから別のウィンドウでアプリを起動するときの設定で、Activity の launchMode が関連してくるというところ。

タスク管理上、launchMode が singleTask な Activity はひとつのタスクでかならずひとつの Activity のインスタンスになることをうまく使って、既に起動しているアプリを片方のウィンドウから情報を飛ばして連携しているように見せることが出来るようになる感じですね。

お互いのアプリがドラッグアンドドロップ機能を実装していればそれに従えばよいですが、ブラウザのように、View のドラッグアンドドロップというわけには行かないパターンもあるので、そんな時はこの方法でドラッグアンドドロップと同等のことができるようになりそうです。

2. tsuyoyo - Tips to learn "DI"

DI の学習には Spring Boot がよいぞ、というお話でした。

オブジェクトを外から差し込むことで、ユニットテストがしやすくなったり、設計がクリアになったりするよ、というのが DI のプラクティスですが、そのプラクティスは言語によっても温度差があるので、そのギャップを埋めるのに Spring Boot は程よい教材となっているようです。

3. shihochan - New Layout Editor with ConstraintLayout

Google I/O 2016 で発表された、新しいレイアウトです。本格的に、GUI のみでレイアウトを組むことが出来るようになる日も近いのかなと感じます。 マテリアルデザインがトレンドになっているため、これに準拠したレイアウトを自動で組んでくれるそうです。

これまでのレイアウトでは、配置の方法に応じて RelativeLayout や LinearLayout などを様々に組み合わせてきましたが、ConstraintLayout を使えばある程度はこれまでの使い分けを気にせずに使えると思います。ただ、どの程度柔軟性があるのか未知数な部分もおおいので、ConstraintLayout の得手不得手は今後も見極めないといけないのかなと思います。

個人的には Swing でもひどい目に合ったので、xml サイコーと思っていましたが、GUI でシンプルに完結するのであれば、待ち遠しさがあります。

4. shaunkawano - Delightful Android DB Development

memo: - Android の DB の実装 - SQL がベタ書き - ボイラプレートだらけ - 型安全とは何だったのか - SQLDelight provided by Square, Inc. - SQL チェックできる。Java から分離できる、モデルが勝手に作られる - IDE プラグイン構文解析 - SQL は .sq ファイルを作る - .sq ファイルはモデルを作りたいパッケージと同じ構造のディレクトリにいれる - SQLDelight + AutoValue + Retrolambda = super short code

AndroidSQLite を使う時のボイラプレートを減らすお話でした。

SQLite をつかってデータベースを作るときには、たいていどの入門書でもStringSQL をベタ書きにしたものをサンプルとして提示していますが、実運用する上では、Java のコードのなかに突然 SQL が現れたり、頑張って文字列結合で SQL を構築したりしていて、「型安全とは…」とか、ミスタイプの恐怖がつきものでした。

そこで GitHub - square/sqldelight: Generates Java models from CREATE TABLE statements. を使うことで、SQL を独立したファイルで管理し、プラグインで入力を補完、さらに自動で対応するモデルクラスまで作ってくれるという画期的な仕組みが導入できるとのこと。非常に嬉しいですね!モデルクラスの生成時にどこまでモデルクラスが責務を負っているかが気になりますが、単純なデータ構造を表すものならば、SQLDelight はその名の通り Delightful なライブラリとして使い勝手良く利用できそうです。

5. hydrakecat - 5 RxJava Tips You Might Not Know

RxJava における実践的な使い方の事例集でした。

Subscriptionの管理はよくCompositeSubscriptionを使っていますが、それ以外にもSubscriptions.empty()もあるということ、またSubscriptionのライフサイクル管理として、SerialSubscriptionを使うことで、新しいSubscriptionをセットすることで都度以前に持っていたものが自動で unsubscribe されるというのはなるほど便利なものがある!という感じでした。この他にも、concat().first() で、複数の Observable を組み合わせ(合成)、はじめに条件にマッチしたものから値を取り出すというロジックが書けるのはとてもうれしい気がします。

各アプリで仕様も異なるため、一概にこれだという解はなかなか出なさそうですが、似たようなことをしようとした時にどんなやり方があるかをリファレンス出来る物があるとすごく嬉しいと思います。組み合わせ次第で無限に出来ることがあるので、何をどう組み合わせるとよいのか、あるいはよくないのか、別の方法ではどうか、などがまとまったものがあるといいなと思っています。

6. woshidan - メモリリークに関するウワサの今昔(仮)

昔語られたメモリリーク対策は今も有効に働くか、というお話でした。

Android における Context の問題は今も昔も変わらず悩ましい問題です。どの Context を使うか、と言う話は、定期的に盛り上がっていますのでそちらを参考にしてください。

ytrino.hatenablog.com

Context, What Context? - by Dave Smith of Double Encore

Yukiの枝折: Android:引数はthisか?getApplicationContextか?ActivityとApplicationの違い

リークという観点では、どの Context を使うか、という以外にも、staticでないインナークラスの取り扱いも問題になります。 内部的に WeakReference で参照を持っていてくれればよいのですが、そうではない場面も多々あります。

Android Framework では、addXXXに対応するremoveXXXや、registerXXXに対応するunregisterXXXといったようなメソッド名の命名によって、使用者側の責任で参照を管理するよう要求している API もあります。コレクションで複数のコールバックインタフェースを管理する場面でよくみられるようですので、この辺りを気にして見てみるとよいかもしれません。

7. OE_uia - Android BLEのつらみ予防

BLE はチップセットに依存して安定度が変わるので、そこを気にして作りを考えよう、と言うお話でした。

そもそもそれは BLE でやるべきなのか、と言うところから(こまめに接続・切断を繰り返し続けるのは、それはそれでコストになる)、このバージョンの Android ではサポート状況が不完全、などなど。あまり BLE には馴染みがなかったですが、今後自分で触る機会があったら気をつけようと思いました。

まとめ

写真とるの忘れました😩

Android Dialogs!

DroidKaigi 2016 の初日に収録していたのですが、ついに Android Dialogs のチャンネルに収録回がアップロードされました。

DroidKaigi で、よく知っている Android Dialogs というチャンネルに出られたのは本当に貴重な体験でした。自分で自分のビデオを見るのはむず痒い感じしかないですが、収録中はとても楽しく会話ができてよかったです。