Infinito Nirone 7

白羽の矢を刺すスタイル

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

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

27th birthday

本日無事に27歳になりました。そして、Drivemode の日本ブランチを登記したのはちょうど一年前の今日なので、日本ブランチは1歳を迎えました。

26歳の間に DroidKaigi が2回開催され、そのどちらも運営として関わってきましたが、特に2回目のときは海外の人たちとの交流が出来て最高でした。 26になった当初思い描いていたこととは多少違う部分もあったりしますが、27歳も最高と言える感じになるように頑張りたいと思います。

ちなみに DroidKaigi では Android Dialogs の収録をしたので、そのうち YouTube に流れてくると思います。

最後に、例の干し芋のリスト: http://www.amazon.co.jp/registry/wishlist/2Y880HXG49I0M

DroidKaigi 2016 の運営をしてきた

去年の DroidKaigi 2015 でもスタッフとして参加していましたが DroidKaigi 2016 でもスタッフとして参加させていただきました。

前回は必要な物の調達と当日の写真係でしたが、今回は広報係として、twitterfacebook ページの中の人になりました。

事前準備

当日はきっとなんやかんやバタバタするんだろうなと思っていたので、予め決め打ちで twitter に流せるものは tweetdeck のスケジュール機能を活用して入れておくことにしました。セッションの開始 10 分前に各セッションの予告を流すのと、朝の挨拶、一日の終りの挨拶を入れてありました。

このおかげで当日はその時必要なタスクに集中できました。よかった。ただ、スケジュールした時刻の AM と PM を間違えて夜中にセッションのお知らせを出してしまったのは反省。

当日

何はなくとも tweetdeck で常に DroidKaigi でエゴサしたタイムラインを流し、スパムを見つけた端から狩っていきました。 初日の基調講演の最中にトレンドに入ったのを確認したので、そこからハッシュタグやキーワードを含めたスパムツイートをしているアカウントを通報して回りました。

それ以外では、落し物の連絡を twitter に流したり、各会場の雰囲気やおやつ・お弁当の写真を上げたり、企業ブースの様子もちょいちょい上げたりしました。

こう見ると結構遊んでますね……

当日「これも twitter に流したいな!」という要望が突発的に出てくることが多々あって、必要な物は事前準備でスケジュールしておいて本当に良かったなと思いました。

それから、海外のエンジニアの方で未だ DroidKaigi の存在を知らなかった方々からもいろいろリプライをもらうことが出来て地味に嬉しかったです。

後日

実はまだ全てのお仕事が終わったわけではありません。セッションの各動画をこれからアップロードしたり、資料のまとめをあげたりと、いろいろお知らせすることがあります。 これらは順次手を付けて行きます。

おわりに

Twitter の運用に関しては、初日こそスパムがいろいろ出てきましたが、二日目は完全にスパムがいなくなったので平和でした。tweetdeck が便利すぎますね。

個人的には、DroidKaigi の公式アプリの開発にも関わることが出来たし、海外スピーカーの人たちと話す機会もあって、楽しい DroidKaigi になりました。 懇親会で寿司🍣とビール🍻をおいしく楽しめて良かったです。

余談

そんな日本語教えたの誰…… 😂

2015 Year Review

こちらアメリカ西海岸はいま大晦日です。いよいよ本格的に 2016 年が来そうなので、その前に 2015 年を少し振り返っておこうかと思います。

仕事としては、Drivemode の日本法人が立ち上がってオフィスが引っ越したというのと、DroidKaigi 2015 の運営スタッフとして、一つの大きなイベントに携わったことがありました。 あとは、Qiita に記事を書きまくったり、potatotips や Android All Stars に登壇したりと、とにかく Drivemode の知名度を上げてこ☆なことを個人的に楽しんでいたりもしました。

Qiita: http://qiita.com/KeithYokoma SpeakerDeck: https://speakerdeck.com/keithyokoma

今年こそはと思って、年末には Android Advent Calendar に登録し、無事投稿することが出来ました。 Qiita のコントリビューション数が 10,000 を超え、未だに1年以上前の記事にもストックが付いているのを見ると、いい加減メンテしないといけないなぁと思いつつ、なかなかできていないので、来年はそのあたりも拾っていけるようになると良いと思っています。 それから、仕事で見ている領域がかなり尖っているのもあって、かなりニッチな記事ばかり書いていた気がします。GitHub もニッチなものが増えているので、もうすこし汎用的なものが作れたら良いなぁ。

趣味の面では、自転車で色んな所に出かけました。日帰りで100km以上の小旅行は、クタクタになりつつも色んな景色を見ることが出来て楽しかったです。

うまくできたこと、できなかったこと、いろいろあって、それぞれに学びがあり、もっと良くしていきたいと思ったこともある反面、まだまだ至らない点がたくさんあるなと感じた一年でした。少しずつステップアップしていけると良いかな。

2016 年も引き続き、よろしくお願いします。