なるようになるかも

力は多くの場合、その人の思いを超えない。

Android Architecture Components 雑感2。

主に LifecycleLiveData について。

Lifecycle

某ポエムサイトにあったクラス図がしっくりこなかったので、自分で書き直してみました。

f:id:quesera2:20170529210511p:plain

Lifecycle のキモは、ライフサイクルメソッドのオーバーライドはもうやめましょう、ということ。

なぜかというと、ActivityFragment を容易に Fat にする上に、処理を追うのが直感的ではなくなってしまうから。

その代わり、ActivityFragmentState を持つように変わります。ライフサイクルイベントが発生すると、どこからともなく(後述) Event が投げられて、これを受けて State の更新が行われます。

StateEvent は公式サイトの以下の図の通りです。

f:id:quesera2:20170529211323p:plain

Handling Lifecycles より)

このイベントの変更を受け取りたいクラスは、LifecycleObserver インターフェースを実装します。これはマーカーインターフェースです。任意のオブジェクトが LifecycleObserver になれます。

その上で、@OnLifecycleEvent アノテーションを付与して、実際のそれぞれの State 変更時の処理を実装します。

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void onResume() {
    // RESUMED State に遷移したときに行って欲しい処理
}

あとはこの LifecycleObserver を、ライフサイクルイベントを受け取りたい LifecycleOwnerActivity または FragmentService)の LifecycleRegistry に登録して、購読させます。

これにより、画面を構成する部品単位で、ActivityFragment から切り離された状態で、ライフサイクルに応じた処理が書けるようになります。

同じライフルサイクルメソッドに、複数の処理が書かれることがないので、コードは読みやすくなりますし、LifecycleObserver マーカーインターフェースを実装するだけでよいので、再利用性も高くなります。

  1. 任意のコンポーネントがライフサイクルを持てる
  2. Activity にベタに書かなくて良い
  3. 再利用可能

という特徴から、「不可視の Fragment じゃん」という感じですが、実装上でもそのテクニックが使われています。

LiveData

さて、LiveData はリアクティブな感じで値を通知する簡易なモジュールで、Rx のオペレータが使えないので正直メリットを感じないものなのですが、LifecycleObserver を実装している、という点は見逃せません。

例えばバックグラウンドで通信を行う場合、結果を表示する UI が既にない、という状況はありがちです。

LiveData は 「STARTED または RESUMED でない状態ならば、値を通知しない」という仕組みになっています。購読、購読解除については、instanceofLifecycleOwner であれば勝手にやってくれると思いますので(たぶん)、あんまり細かいことを気にしなくていいのがウリです。

類似品に、RxJava の親戚の RxLifecycle がありますが、こちらは RxFragment のようなライフサイクルを監視する特定のクラスを継承しなければいけないのに対して、Lifecycle は Andorid の仕様の穴を突いたような素敵実装になっており、ActivityFragment の実装を選ばない、という点がメリットとして挙げられると思います。

とはいえ、個人的には使わなさそうな感じです。

重要なのは、今までのライフサイクルメソッドを太らせていく記述方法から、データドリブンな記述(Rx に近い考え方)へ転換しましょうね、というのが LivecycleLiveData の狙いなので、もし Rx があんまり好きじゃないならスルーしていいと思います。

Lifecycle の実装

www.jianshu.com

内部を追いかけていたらこのサイトに行き当たって、知りたいことはほぼほぼ説明されていました。

Android Architecture Components のソースコードはまだ公開されていないので、現時点で内部構造を語っているのは世界で唯一このサイトだけだと思います、たぶん。

registerActivityLifecycleCallbacks()Activity のライフサイクルコールバックを受け取って、onActivityCreated() で不可視の ReportFragment を埋め込んでライフサイクルを監視してるわけですね。

この ReportFragment のライフサイクルメソッドの呼び出し時に、

private void dispatch(Event event) {
    if(this.getActivity() instanceof LifecycleRegistryOwner) {
        ((LifecycleRegistryOwner)this.getActivity()).getLifecycle().handleLifecycleEvent(event);
    }
}

という感じで、もし ActivityLifecycleRegistryOwner を実装していたら、getLifecycle().handleLifecycleEvent() を呼び出すという単純ながら力強い実装になっています。

registerActivityLifecycleCallbacks() を行うタイミングがないように思えますが、LifecycleRuntimeTrojanProvider というダミーの ContentProvider で実現されている、というのが面白ポイントです。ContentProvider がアプリケーションプロセス起動時に立ち上がるのを利用しているわけですね。

ViewModel と画面遷移

いまだにしっくりこないのが、ViewModel で画面遷移をどう実現するか?という部分です。

Handling LifecyclesViewModel のベストプラクティスの項目には、

Never reference a View or Activity context in your ViewModel. If the ViewModel outlives the activity (in case of configuration changes), your activity will be leaked and not properly garbage-collected.

【私訳】 ViewModel に View または Activity context への参照を持たせないでください。もし ViewModel が Activity より長生きした場合(configuration changes の発生時など)、Activity はリークして、適切に GC に回収されません。

とある通り、ViewModel に画面遷移に必要な Context を持たせたくないのですが、とはいえどうすればよいのか。

公式のサンプルだと、NavigationController を DI しているのですが、これは単一の Activity だからできるという感じだし、画面の数だけ navigateToXXX() メソッドを増やしていくのは微妙だなーという感想しかないわけです。

実際的には、Intent フラグを与えたり、Bundle でパラメータを受け渡ししたいというようなケースも多々あるので、もうちょっと汎用的に使えるものが欲しい。

また、このサンプル実装だと、commitAllowingStateLoss() で誤魔化していますが、FragmentManager もライフサイクルと切っても切れない Android のつらい問題のひとつで、ViewModel のスコープから画面遷移をキックすると、IllegalStateException に悩まされるのがオチだと思っていて、その辺のライフサイクルの問題までは、まだまだ解決しそうにないなーという感想なのでした。

Android Architecture Components 雑感。


Architecture Components - Introduction (Google I/O ‘17)

解説はこのビデオを見るのがとても分かりやすいです。

機械翻訳で日本語字幕が出せるので、英語が聞き取れなくてもだいじょうぶ。

いままでの Android の世界観

※人によってかなり違う気がします

f:id:quesera2:20170524223955p:plain

  • バックグラウンド処理は Service を使うことができました
    • Service をキックするのは Activity でもいいし、SyncAdapter とかを使う手もあったり
    • この辺使うのが面倒だったりで、AsyncTask の不治の病に陥ってたり、RxJava 使ったりの方が多いですよね…
  • ContentProvider を経由することでデータベース実装を抽象化
    • やってるの、正直あんまり見たことない…
    • 連絡帳プロバイダとかにちょっと凝ったクエリ(グルーピングとか)を投げようとすると、途端にテーブル構造を意識したハックみたいな記述になって全然抽象化されてない説がありますよね
    • Android 2.2 とかの時代に、android:exported (外部公開するかどうか)のデフォルト値が true だったせいで迂闊に使うと脆弱性があるみたいなイメージが付いたのが悪いんでしょうか…
  • データの取得・更新反映は CursorLoaderContentObserver を使えばおおよそ解決
    • これは割と普通に便利だと思うんですよ。Loader 単体でテストも書けるし

これからのざっくりとした感じ

f:id:quesera2:20170524230321p:plain

  • ライフサイクル問題の解決として、ViewModel が登場
    • 他にもなんかいろいろあるっぽいけどあんま追えてないです
    • Activity または Application のライフサイクルで生存するインスタンスみたいな感じ
    • ViewModel はあるものの、MVVM フレームワークにこれといった決定打がなく、また DialogFragment なんかをどうすればいいのか?という部分までは解決できていないという印象
  • データがローカルにあるのか、あるいは通信で取得するかはリポジトリで隠蔽
    • リポジトリLiveData を返す、LiveData は通信や DB アクセスを行った結果をリアクティブな感じで渡す
    • ちなみに現状で、Data Binding もしたい場合は、ObservableFieldLiveData の変換をどうにかしないとダメっぽい、辛い
    • ざっくり使ってみた感じだと、LiveData は正直使う必要性を感じない、普通に RxJava で良いと思う
  • 公式 ORM として、コードファーストな感じで DB を操作できる ROOM が(今更)登場

あくまでこれらは指針

念頭としてあるのは、開発者が行うべきことは、Activity のライフサイクルと格闘したり、FragmentManager の実装の深淵を追いかけたりすることでもなく、アプリケーション開発を通じてユーザーに価値を提供するということ。

その方法論として、「Andorid 標準のコンポーネントを使って、無理に Android のルールで拘りすぎる必要がない」ということが提示された、というのが一番大きいのかなぁと思いました。

Volley とか Loader の頃とかはまだ、Android のルールの上で利便性を高めることが意識されていたように思うのですが、ユーザーの間で発展していった設計思想なんかを、民主主義的に取り入れていくというメッセージ性を感じます。(Kotlin 公式言語化にも通じるところがあり)

Architecture Components 自体、強制されるものではなく、部分的に取り込んだり、より発展させることができる、非常に高い自由度があるサポートライブラリになっていて面白いです。

「Android を支える技術 <I>」を読んだ。

この本いいよね…。

今 <II> も読んでるんですけれど、「uid ってどこで作られてるの?」っていう5年くらい疑問だったところが解決されてとても嬉しい。gid は意味なさそうなのになんで設定されてるの?っていうところまでは書いてなかったけど。

この本はものすごく技術書なんだけど、それでいて単純に読み物としても非常に面白いのですよね。

ガラケー時代の組み込みのようなアプリを作ってきた世代と、AndroidiOS でアプリを作り始めた世代の架け橋みたいな感じの内容というか、まさに自分が Linux カーネルとかいまいち分からず、Andorid のソースコードを読み解こうとしても低レイヤの世界で何が起きているのか完全に分かっていなかったところがあって、この本の内容はクリティカルヒットな感じでした。

ただ、アプリ開発しているような世代の人も、いまやスマートフォンコモディティ化して、アプリ開発の大規模化や長期化が進んだことで、「どうやってアプリを差別化してバリューを生み出すか」とか「どういう設計にすればアプリを保守していけるのか」みたいな、もっと上位レイヤな方向に興味が向かっていると思うのですよ。

この本がそういう方向で役に立つということはきっとなくて、でも、だからこそ、今このタイミングで、この熱量の Android 本が出てくるというところが感無量ですよね。

以下、サブタイトルが気になったので思うところを。

Project Butter

Android が「60 fps を実現するモダンな GUI エンジン」を目指していたのかというと、別にそうでもなかった気がします。

2.X 時代は GC で普通に固まってたから、Stop The World を回避するためにオブジェクトを作り置きするのが基本だったし、それで処理落ちを頑張って減らしたら実は液晶が 56fps しか出ないことを知って悲しみにくれたりしてました。

「ぬるぬるな iOS に対して Android はもっさり」が半ば諦めとして定着していたなかで、いよいよ Google が本気を出して改善に取り組みました。それが、Project Butter と呼ばれるものです。

しかしながら、この本では全然出てこない気がします。というのも、Project Butter が大々的に謳われていた JB の扱い自体がヒドいのです。曰く、「通常の使用に耐えなくなる」とか「停滞感と閉塞感」とか「開発者も普通っぽい」とか。

しかし、垂直同期が取れる Choreographer クラスが登場したのは JB です。それまでの Android は、なんと垂直同期なしでダブルバッファリングをしていたのです!バックバッファとフロントバッファの切り替えは何のタイミングでやってたの…?

これとは別に、トリプルバッファリングの仕組みも出てきました!(P233 を見るとこっちが現行の動きみたいですね)

垂直同期を取ってダブルバッファリングで描画する方法は、描画命令が 16ms 以内で収まる場合には理想的な 60fps になりますが、少しでも描画が間に合わなくなると、前フレームの描画でバックバッファがロックされている関係で、一気に 30fps に落ち込んでしまうという欠点もあるのです。

半分のフレームになるので、処理落ちした途端にガクガクになったように見えます。

これを抑制するのがトリプルバッファリングです。描画可能なバッファを常に裏に 2 枚持つことで、処理落ちでジャンクフレームが生まれても、フレーム落ちを滑らかにして分かりにくくする、という技術です。当然、画面 1 枚分の余計なフレームを描画するために、無駄な GPU とメモリを消費します。

JB の評判がすこぶる悪かったのは、そういう富豪的な改善方法に問題があったからなのでしょう。

Project Svelte

これに対して、機能を維持しつついかに軽量化するかというのが Project Svelte です。

この本にも KitKat で劇的に立て直したという話が出てきますが、その過程がとても面白いのです。メモリが 512 MB の改造 Nexus 4 を作って、開発チームはそれを個人の端末として日常的に使ったとかどうとか(KitKat開発者が語る、Android軽量化の裏話)。

これは現在進行形で、Android O でも引き続いてバックグラウンド処理の取り締まりなどで強化されていっています。

しかし、裏でどんだけ電力食おうが許される野放図な感じとか、暗黙の Intentでゆるくアプリ同士が繋がるのは、Android の魅力でもあったと思うので、どんどん世知辛くなっていくのは、ちょっとだけ寂しい感じもしますね。

なお、普段使いは iPhone なので、Pixel が日本で発売されるまでは、この辺実際どんな感じなのか知りません!

追記

<II> も読み終わりました。

アプリケーションの世界で閉じている iOS に対して、ActivityServiceContentProvider というコンポーネント単位で横断して連携できるアプリを、Intent という共通したメッセージングで行えるところが Android のよさだと思っていて、ちょっと Activity と画面遷移の話に寄りすぎかなぁという感想もあったりしましたが、おもしろかったです。

もうちょっと Linux カーネル力があれば理解度が上がったのになぁ…。

ところで、<I> の6章のグラフィクスアーキテクチャの話については、AOSP の Porting - Graphics を読むとより面白い気がします。おすすめ。

「よくわかる AutoLayout」読んだ。

よくわかるAuto Layout iOSレスポンシブデザインをマスター - リックテレコム書籍情報

前提

わたしはコード、VFL、Storyboard で制約を書きます。ライブラリは特に使いません。

なんで全部覚えたの?って疑問に思うかもしれません。わたしも最初はいずれかひとつだけ身に付ければいいと思っていましたが、いまはそれぞれが相互補完する関係にあると思っています。

  • コードによる制約は、初見では複雑ですが、慣れればそうでもないです。付与できる制約に制限がないのがメリットです。
  • View の位置関係を示す場合、VFL は非常に簡潔に記述できます。覚えておけばデバッグ時にも便利です。ただこれ一本だとできないこともあります。
  • Storyboard/InterfaceBuilder による制約の付与は、プレビュー機能など利点が多いです。ただし込み入ったレイアウトを実現するいくつかのハックを知っている必要があります。
  • 暗黙の制約(translatesAutoresizingMaskIntoConstraintstrue のままにする)も割と便利です。ドキュメントには autoresizing mask で指定した振る舞いを duplicate するとあるのですが、これの意味は未だに分からんです。

適材適所でうまく組み合わせるのが重要だと認識しています。

UPDATE: 暗黙の制約の仕様は Xcode7 と Xcode8 で別物になるらしい。いままで autoresizing mask ガン無視だったのが、ちゃんと反映されるようになるとか。まじかよ。

本の概要

iOS 開発を初期からやっている人はきっと必ず本棚にあるであろう、レジェンダリィな UIKit 本の著者が技術監修をしている、AutoLayout 攻略本です。

公式リファレンスもあります

developer.apple.com

そもそも AutoLayout 攻略本が必要なのか?公式ガイドで十分でないか?という疑問があると思います。

Apple の公式には日本語訳されたガイドがあるのです。しかし、「固有の寸法」「不定なレイアウト」「収縮性/膨張性の優先度」など、個人的にはピンと来ない感じの訳なので、初学者にはお勧めできません。

少し前に読んだ「SwiftではじめるUI設計&プログラミング」は、この辺の言い回しを、日本語として分かりやすく表現する苦労が感じられて良かったです(UI 設計の本というより、初級者向けレイアウトの作り方の本なのでそういうタイトルの方がよかったのでは?というのと、Swift1.X、iOS8 の本でなければ…という感じ)。

読書感想文

いわゆる、個人の感想ってやつです。

よかった点

硬派な内容

原色強めの表紙やタイトルから、なんとなく初級者向けなイメージを受けましたが、複雑なレイアウトを実現するためのパターン、実践的なハックも網羅されています。

ハマりがちな Self-Sizing Cells や UIScrollView への制約付与の仕方、さらに無限スクロールしたい UIScrollView への制約の付与の仕方も丁寧な解説があってとても参考になりました。

基本的なビューのデバッグ技法から、AutoLayout 固有の制約のデバッグ方法まで一通り書いてあるので、とりあえずこの本が片手にあればなんとかなりそうな気がします。

AutoLayout のレンダリングの裏側で起きていることを詳述している

ここはまだ自分も理解が追いついていない部分なのでまた読み返しますが…。

AutoLayout において viewWillLayoutSubviews:viewDidLayoutSubviews: は何なのか、とかそういう部分まで掘り下げて書いてある本は和書ではこれしかないと思います。

iOS9 の最新の知識を得られる

まだ iOS8 を切れなくてつらぽよですが、

  • UIStackView
  • UILayoutAnchor

について丁寧な解説があります。iOS10 ではこの辺に大きな仕様変更はなかったので、とりあえずしばらく使える知識なはずです。

よくなかった点

よくわからなくなくない?

基礎的な部分から始まりはしますが、ある程度 AutoLayout で苦しんだユーザーを想定しているような感じです。そういう人が開眼するには間違いなく良書だと言い切れます。

一方で、これから Swift を初めて、ちょっとした iOS アプリを作ってみよう…という人には敷居が高すぎる本だとも思います。

3章から UIWindowmakeKeyWindow()、ルートビューコントローラーみたいなトピックが入ってきますが、iOS7 から始めた人はたぶんこの辺のおまじないの話で辛くなってしまうような…。

トレイトコレクションを理解する上で、UIWindowUIScreenUIViewController がどういう関係にあるのか、という知識は必須なので、AutoLayout 自体がむずいってのが根本的な原因な気がしますけれど。

Width/Height Attribute に対する誤解

21ページにあるこの記述。

・Width、Height
Width、Height はそれぞれ、インターフェースオブジェクトの幅と高さを示します。この Attribute を用いる場合、この制約は1つのオブジェクトで完結するため、制約の SecondItem は nil になります。

これは根幹的な間違いだと思うのですけれど、わたしの誤読?

EqualWidth や EqualHeight の制約をコードで書くには SecondItem に同じ幅・高さにしたい View を指定します。

コードで作る制約は VFL と違って Multiplier を指定できるので、親 View とのパーセンテージでサイズ指定することをできます。個人的には、Adaptive なレイアウトを作るうえでは欠かせない知識だと思っています。

サイズクラスの話が本格的に出てくるのが遅め

レスポンシブデザインを語る上で、AutoLayout と SizeClass は不可分だと思うのですけれど、「サイズクラスとトレイトコレクション」は一番最後の章になっています。

名前そのものは、2章でも「Adaptive なレイアウトを構築する上で重要だ」として挙げられてはいます。

ですので、この本は AutoLayout にフォーカス絞り、そこから SizeClass まで話を膨らませないという組み立てにしたのかもしれません。

Android 7.0 では TextureView よりも SurfaceView が推奨されるらしい。

まじかよ!ってなったので。

たぶん、大半の人にとってはどうでもよいです。

公式リファレンスの Android N for Developers の英語版には以下の記述があります。(いまのところ日本語には訳されていないです)

Android 7.0 brings synchronous movement to the SurfaceView class, which provides better battery performance than TextureView in certain cases: When rendering video or 3D content, apps with scrolling and animated video position use less power with SurfaceView than with TextureView.

The SurfaceView class enables more battery-efficient compositing on screen, because it is composited in dedicated hardware, separately from app window content. As a result, it makes fewer intermediate copies than TextureView.

A SurfaceView object's content position is now updated synchronously with the containing app content. One result of this change is that simple translations or scales of a video playing in a SurfaceView no longer produce black bars alongside the view as it moves.

Starting with Android 7.0, we strongly recommend that you save power by using SurfaceView instead of TextureView

Android 7.0 のマルチウインドウ環境において、SurfaceView はハードウェア上でアプリケーションの Window と合成されるため、バッテリ効率が非常によくなっているのだそうです。

このため TextureView の代替として SurfaceView の使用が強く推奨されるとか。

SurfaceView って何?

Android 2.X の古代において、普通の View を描画するよりも高速だったものです。

原理としては、別スレッドでバックバッファの描画を行い、通常の View 階層ではなく独自の Window に描画を行います。

Android 3.0 でハードウェアレンダリングが一般の View でも使われるようになり、4.0 でデフォルト化すると、ソフトウェアレンダリングを行う SurfaceView は通常の View より低速になったと言われてます[要出典]

OpenGL で描画を行える、GLSurfaceView もありましたが、ほぼ OpenGL ES 1.0 なので、奇特な人しか使ってないと思ってます。RSSurfaceView のことは、忘れてあげてください。

TextureView って何?

Android 4.0 で鳴り物入りで登場したものの、いまいち流行らなかったやつです。

SurfaceView と似たようなインターフェースを備え、Canvas の簡単な APIOpenGL ES の高速描画を享受でき、それだけでなく通常の View 階層に組み込んで使え、アフィン変換やアルファブレンドも行える!などなど、利点が超アピールされていた気がします。さりげなく Camera2 API のプレビューでも使われています。

実際のところ、OpenGLBitmap を延々と生成して表示しているだけなのでメモリ消費が半端なく、生の OpenGL ES 2.0 が分かるならそちらを使った方が効率がよい上に、分からない人にも Unity などのゲームエンジンが普及したので、どの辺の層が使っているのか謎な感じです。

ともあれ、SurfaceView の強化版のような触れ込みで登場しましたが、両者は全く別物ということだけは言えます。

なぜ TextureView が必要とされたのか

Android 4.0 Graphics and Animations によれば、

Because a SurfaceView’s content does not live in the application’s window, it cannot be transformed (moved, scaled, rotated) efficiently.

SurfaceView の弱点はアプリケーションの Window に属していないので、効率的な変換が不可能だったということにあります。

しかし Android 7.0 でマルチウインドウ化するとともに、

A SurfaceView object's content position is now updated synchronously with the containing app content.

SurfaceView がアプリケーションのコンテンツと同期を取れる(※ただし Android 7.0 以上のみ)能力を持つに至り、もはやバッテリーを食うだけの TextureView は不要、みたいな感じなんでしょうか。

これに伴う混乱

SurfaceViewTextureView で検索するとこういう感じの情報が得られます。

  • Android 2.0 時代に書かれた「SurfaceView は高速にレンダリングできる View である」という極めて古い資料
  • Android 4.0 時代に書かれた「より新しい TextureView に乗り換えるべき」という期待に満ち溢れた古い資料
  • Android 5.0 時代に書かれた「TextureView はトラップなので使うべきではない」という怨嗟を感じるやや古い資料
  • Android 6.0 時代に書かれた「SurfaceView は低速なので使うべきではない」という妥当だけどタイトルが裏目に出ている資料
  • そして、Android 7.0 時代に書かれる「SurfaceView はバッテリ効率が良いので TextureView より推奨される」という資料

何を信じればいいんだ…。

個人的な結論

SurfaceViewTextureView も今となっては使うべきではなく、Canvas API で独自の描画をしたいならカスタム View を作ればいいと思います。

カスタム View では実現できない高速な描画が必要?

流行の最先端である Vulkan Graphics API を覚えるのがお勧めですよ!!!

ここ最近読んだ技術書籍感想文。

雑多に読んでます。

リンクは書籍の公式サイトです。

黒帯エンジニアが教えるプロの技術 Android開発の教科書

SBクリエイティブ:黒帯エンジニアが教えるプロの技術 Android開発の教科書 (ヤフー黒帯シリーズ)

幅広いトピックを扱っているのだけれど、それゆえにどこかで読んだ本の内容を簡略化しているだけの章もあったり、開発以外のトピックの割合が多かったり、Andorid 開発の高度な技術トピックを期待すると中途半端かもしれないという感想でした。

同時期により初心者向けと思われる「基本からしっかり身につくAndroidアプリ開発入門 Android Studio 2.x対応」という本も出版されていたので、こちらのトピックでよかったんじゃない?という内容もちらほら。

また、アプリのグロース関連の章については、Firebase の登場で大きく変わった部分が多く、タイミングの悪い感じです。

具体的には、たとえばクラッシュレポートは Firebase Crash Reporting、AB テストは Rule Types and Variables、アプリケーションのプロモーションのチャネルに Firebase Invites が追加され、GCM は FCM になりました。

これらは単に置き換わったのではなく、Firebase プラットフォーム上のサービス間のシナジーが本質で、例えば「バグの対象機種で、最近起動率の減ったユーザーをトピックに、バージョンが上がって改善したことを Push 通知を使ってフォローする」といった面白い使い方ができるのです。

C#実践開発手法~デザインパターンとSOLID原則によるアジャイルなコーディング

ec.nikkeibp.co.jp

Xamarin のために久々に C# やっていて、1年くらい積まれてた本をようやく消化したのですが、今まで読まなかったのを悔やむくらい、適応力の高いコードの書き方について解説している良書でした。

  • 近年に重要になった、「変化に強い」デザインパターンを使ったコードの書き方
  • デザインパターンを支える基礎である、重要なプログラミング原則群の解説
  • 架空のストーリー仕立てで、プロダクトマネージャと開発者がいかに協調し、実際的にアダプティブにコードを作っていくか

という構成になっています。ただし、翻訳は結構眠くなる気がします。

ダンC# 向けの CleanCode という感じの本です。

Javaプログラマーなら習得しておきたい Java SE 8 実践プログラミング

book.impress.co.jp

これからの Android 開発は、Kotlin か Java8 で頑張るかの二択になると思いますが、付け焼き刃の新言語を使うまでもなく Java8 で十分いける気がしてきました。

記述が冗長という欠点については、IntelliJ IDEA の超強力なサジェストがさくさく代筆してくれるので、あまり気にならないです。

この本は、

を読んだ人向けに、Java SE7 と SE8 との diff をざっくり理解するのには良い本です。翻訳者が同じなので。

ただ内容は非常にあっさりとしていて、それを補うためか章末問題が用意されています。この問題は一部が難解というか、この本の内容だけで解けるとは到底思えないものもあるので、かなり苦しむことになります。

しかも解答が載っていないのですが、この章末問題に挑戦している人は国内外にいっぱいいて、解法を GitHub などで共有していたりするので、自分が苦しんで得た解答を他人の解法と照らし合わせて、遥か高みにいるなーとか、またさらに他の人は別のアプローチで解いていて読んで唸らされたり、というのが本当に楽しいです。

そこまでやれば、Java8 実践レベルの知識を得たと自負していいはず。

Android Studio本格活用バイブル~効率的にコーディングするための使い方

gihyo.jp

Android Studio 対応」を謳う入門書は多々ありますが、この本が他の Android Studio 本と一線を画しているのは、Android 開発をやりたい人向けではなく、IntelliJ IDEA での効率的なコーディング方法を知りたい人向けに特化しているということです。

正直、この本のタイトルは詐欺だと思いますが、いい意味で騙されました。

Eclipse ADT から Android Studio へ移行したとき、Darcula テーマのモダンさから「新しいクールな IDE になった」と捉える向きがありました。

この本はそうではなく、IntelliJ IDEA も Eclipse と同じくらい歴史のある古い IDE とみなしています。長い歴史のある IDE はしがらみのためにショートカットに癖があったり、あるやり方をするのに複数の方法があったり、洗練されていない部分もあることを認めつつ、しなしながらその歴史が育んだ効率的なコーディング方法をいかに手に馴染ませるかということを滔々と解説しています。IntelliJ 愛してるなーという感じです。

反面として、レイアウトエディタの詳細な使い方とか、メモリリークの調査方法とかそういう Android 開発をする上で実際的に必要になる使い方については書籍版では触れていないです。Android Studio のコーディングの操作量を上げたいという人に向けた一点突破の本です。

SwiftではじめるUI設計&プログラミング 「操作性」と「デザイン性」を兼ね備えたアプリの開発手法

www.shoeisha.co.jp

この本は、Swift 1.X & iOS8 なので今から読むには微妙だと前置きします。

しかしながら、個人的には、AutoLayout や SizeClass に関する説明は割と分かりやすかった気がします。全編フルカラーで、UI カタログ的な章もありますので、iOS SDK にどういう UI コンポーネントがあるのかまだよく分かっていない人に向けていい本だと思いました。

評判の良いよくわかるAuto Layoutも買ったので読み比べる予定です。

スマートフォンの連絡帳の話。

純正の連絡帳で別に満足してるって人もいれば、オリジナルの連絡帳を入れてる人もいますし、そもそも連絡帳に何も登録されてない人もいるでしょう。

それはさておき、iOSAndroid の OS 標準の連絡帳はユーザー視点で見るか、開発者視点で見るかで貧弱とも言えるし、高性能とも言えて面白いです。

連絡帳の基本思想

iOS / Android に共通して言えることとして、実用的ではないです。グルーピング表示すらできません。

その理由は、OS 標準の連絡帳は、端末内の利用出来る情報を活用しつつ、様々なアプリへ連絡帳情報を提供するためのデータベースとなるよう、割り切った実装になっているからです。

連絡帳の元データは、多種多様です。

Android であれば Google アカウント、iOS であれば iCloud アカウントに紐付いていることが多いと思います。

その他にも SIM カードやソーシャルアカウントなど複数のデータソースを持つ場合があります。連絡帳はそれらの情報を収集し、統合した連絡先を表示しています。

例えば適当な数字ですが、Skype のフレンドが5000人いて、Facebook の友達が5000人いて、Google アカウントに5000人登録されていたとします。連絡帳はそれらのアカウント情報の断片から名寄せを行い、統合された7500人のデータを表示します。「自分」が3人に分裂したりすることは、きっとありません。

このような処理はリアルタイムではできません。このため、事前にデータベースに蓄積しています。高速に取得するために、インデックスを貼り、非同期で行われる更新をトリガーに、情報の紐付けを更新しています。これだけ考えるとものすごい高性能です。

そしてグルーピング機能が貧弱なのも説明できます。API 的には iOS でも Android でもグループを設定することはできるのです。ただしデータソース元がグルーピング機能を持っているとは限りませんし、また統合された連絡先をグルーピングすると、異なるデータソース間をまとめてしまう可能性があるので GUI としての提供は限定的なのです。

テーブルから理解する連絡帳

データは SQLite で保持されています。なので SQL の民であれば、ER 図を見れば余裕で理解できるはずです。

Android

いっぱいテーブルがあるんですけれど、だいたい下の3つがわかってれば問題ないという噂。

f:id:quesera2:20160622233507p:plain

contactテーブルに統合された連絡先データがあります。

統合人格は名寄せされた複数のraw_contactテーブルと関連付けられています。

実際の各データはdataテーブルに入っています。このdata1data15という汎用的で素敵なレコード名で察せられるかと思いますが、名前の場合、「data1に表示名、data7に名前のフリガナ」、電話番号だったら「data1に電話番号、data2に種別(携帯か固定電話か)」みたいな感じです。

連絡帳のContent Providerへ問い合わせをするときには、このテーブル構造を覚えておくと少しだけ便利です。ほとんどの場合、単体のテーブルではなく、JOINしたテーブルに対する問い合わせになります。SQL を意識すれば無駄を省くことができます。

以下、いくつかポイントです。

lookup

contactのプライマリキーは_id列ですが、これはただの Android の作法で、実際の一意キーはlookupです。これの使い方にも癖がありますけど、リファレンス読めば大丈夫。

sort_keysort_key_alt

日本のロケールで見たとき、連絡帳は「山田太郎」さんと「Jhon Doe」さんのどちらが上であるのが自然でしょうか?

単純な文字列のソートでは、数字、英語、ひらがな、漢字になります。sort_keyは読み仮名などを考慮した上で、それぞれの国のロケールに合わせたソート順で連絡帳データを並び替えるためのものです。連絡帳データは件数が予期できないので、複雑な条件でソートをするよりも事前にソート用の値を持っておいたほうが楽なわけですね。

sort_key_altロケールに依存しないソートをしたい場合に利用します。

name_lookupphone_lookup

名前や電話番号は表記ブレがおきます。080-3123-2931と書いたり、(080)31232931と書いたり。

名前や電話番号を正規化して保持しているルックアップテーブルが存在します。氏名や電話番号から検索する場合などは、contactテーブルから辿るよりも、こちらを使うほうが適切です。

iOS

iOS の連絡帳の構造はすごく単純です。外部キー制約も一切ないです。

f:id:quesera2:20160622233516p:plain

統合された連絡先情報を、ABPersonテーブルに保持しています。複数のデータを持つ場合(例えば電話番号が自宅と携帯と会社で3つあるなど)は、ABMultiValueでラベルと値を保持しています。

Androidraw_contactに相当するテーブルはなく、統合された結果だけを保持しています。

なので統合された情報が何に由来してきているのかを知る術があまりなさそうで、ABStoreから知ることができる情報も、LocalExchangeCardDAVかみたいな超ざっくりとした情報しか持っていません。

iOS の場合、AddressbookUI/ContactsUI フレームワークが用意されているため、あまり気にすることもないでしょう。

Addressbook フレームワークはほぼ C 言語な感じで、Objective-C からでも使いづらいのに、Swift から使うのはチャレンジャーな感じがありましたが、iOS9 から Contact フレームワークに置き換わって楽になりました。

FirstSortLastSort

この中身は姓名でソートするために事前に計算されたキーです。

A)D=みたいに文字を並び替えていっているみたいですね。FirstSortLanguageIndexLastSortLanguageIndexという列もあって、これを組み合わせてロケール対応しているのでしょうか。

iOS の場合、テーブルへの問い合わせはNSPredicateというブラックボックスを介するので、Android ほど SQL 感はありません。しかしユーザーコード上でソートするよりも、sortOrderを適切に渡した方が何かと無難だとは思います。