概要
-
セミナー名: DroidKaigi 2017
-
日時: 2017/03/09 - 2017/03/10
-
場所: 東京都新宿区西新宿8-17-1 (住友不動産新宿グランドタワー5F)
-
ハッシュタグ: #DroidKaigi
-
CONNPASS URL: https://droidkaigi.connpass.com/event/43942/
-
参加費 8,000円 (アフターパーティ無し)
-
以下、参加したセッションのメモを記載
講演1: ウェルカムトーク / Welcome talk
登壇者
- mhidaka さん
- Twitter ID: @mhidaka
概要
- 参加者への挨拶
- Android 開発者へのアンケート結果の発表
発表内容
- Android 開発者へのアンケート (180人が回答)
-
年齢
- 20代: 46%
- 30代: 45%
- 40代: 6%
-
経験年数
- 15.0% 0.5~1年
- 32.8% 2~3年
- 22.8% 4~5年
- 16.7% 6~7年
-
ビジネス
- 68.2% 自社開発
- 23.5% 受託開発
-
新規 or 差分
- 65.7% 差分開発
- 33.7% 新規開発
-
プロジェクトのメンバー
- 36% 4~5人
- 25.8% 2~3人
- 24.7% 1人
- 7.9% 6~9人
-
リリースペース
- 33.9% 1month毎
- 27.6% 2week毎
- 15.5% 1week毎
-
講演2: Android ORMの選び方
登壇者
概要
-
gfx さんは自分で OR Mapper を作った (Orma)
- OR Mapper 開発するときに、どういうところに着目したか
-
ORM とは
-
ORM の比較
-
Orma をどう設計したか
発表内容
-
ORM とは
- なぜデータを保存する必要があるか
- デバイスの状態/設定を保存するため
- Web API よりも速く、信頼できる
- 何を保存するか
- 設定, キャッシュ, 行動ログ
- Android でローカルに保存する方法はいくつかある
- SharedPreference
- Filesystem
- SQLiteDatabase
- SQLite ラッパー (ORMs)
- 独自のストレージエンジン (Realm, ObjectBox, etc.)
- SQLiteDatabase
- RDBMS
- SQLite
- ORMs (Object-Relation Mappter)
- 実装はたくさんある
- たくさんある理由は、データに関する考え方がたくさんあるので、自分で作りたくなってしまうから
- 実装はたくさんある
- ORM がすること
- O/R Mapping
- DBテーブルみたいなもので管理されたものを、Java のオブジェクトにデシリアライズする
- Java オブジェクトを DB テーブルみたいなのにシリアライズする
- Query Builder
- 別名 SQL Builder
- 開発者が書いた SQL文をセキュアな SQL 文に変換する
- Association (or Relationship)
- 二つのテーブルを関連づける
- DB のカラムとして、他のテーブルを持たせることができる
- Pub-Sub
- データの変更が発生したら、通知を送信する
- モデルの状態を同期を取らせる為にある
- Migration
- DBスキーマとデータを移行する
- O/R Mapping
- なぜデータを保存する必要があるか
-
ORMapper の比較
- ActiveAndroid v3.0
- もっとも有名
- 簡単に使える
- 最早メンテナンスされてない
- リフレクションベースの実装
- モデルの作成として、Model クラスを継承
- Query Builder が洗練されている
- Association はこれ以上ないくらいシンプル
- Pub-Sub は ContentProvider を通じて行われる
- Migration は貧弱
- スキーマを変える為には、アプリのアンインストールが必要
- green DAO v3.2.0
- もっとも有名な ORM の一つ
- 速い
- モデリングが貧弱
- モデルは、変数にアノテーションをつける
- ビルドすると自分が書いたクラスにメソッドが自動付与される
- 自分が書いたコードと自動生成のコードが混在するので、かなり混乱する
- 運用がたいへん
- モデルは、変数にアノテーションをつける
- Query Bulder は普通通りに書けばいい印象
- Association も同様、普通に書けば良い
- 保存する順番は自分で定義する必要がある (ActiveAndroid と同様)
- Migration はサポートされてない
- Requery
- 比較的新しい ORM
- モデルの定義は abstract クラスを自分で作って、それを継承する
- Query Builder は、メソッドが Result クラスを返すのが斬新
- Pub-Sub: RxJava / RxJava2 に対応
- Migration: 自動的に新しいカラムを追加する
- Realm
- モデルの定義: 自分でアクセサを提供する必要がある
- Association, Pub-Sub: サポートされている
- Migration: Java で処理を書く必要があるが、書くのは大変
- Orma
- 2016/01 にリリースされた
- Cookpad, Abema TV, Mercari アッテ、などで使われた
- Query Builder: 検索するには、該当するキーにインデックスを貼らてないといけない、という特徴がある
- Pub-Sub: サポートされているが Experimental
- Migration: Schema-diff マイグレーション
- ActiveAndroid v3.0
講演3: Building my own debugging tool on overlay
登壇者
- KeithYokoma さん (ドライブモード)
- Twitter ID: @keithyokoma
- スライド
- サンプルアプリ
概要
- 開発者オプションからオーバーレイでデバッグ情報を表示させることができるような View を自分で作るための方法について
発表内容
-
使うクラス
- android.view.WindowManager
- android.view.WindowManager.LayoutParams
-
パーミッションが必要
- android.permission.SYSTEM_ALERT_WINDOW
- android.permission.MANAGE_OVERLAY_PERMISSION
-
複雑なので省略するが、Activity の上にオーバーレイで View を表示させるための手順の紹介
-
他のアプリに View を重ねるには、 ライフサイクルを超えて View を管理する必要がある
- → Service を利用する
-
端末のリソースが足りなくなると、 Android 側で勝手にアプリを終了してしまう
- → プロセスをできるだけ長く動かし続けるための方法
- → アプリを Visible Process に昇格させる
-
オーバーレイでデバッグを表示させるアプリは Service で動いている
- Service にデータを渡すには、以下の3つの方法がある
- startService の Intent にデータを詰める
- EventBus を活用する
- Dagger 等の DI でスコープを切る, Rx を使う
- Service にデータを渡すには、以下の3つの方法がある
講演4: Data Bindingで実現するMVVM Architecture
登壇者
- Kenji Abe さん (MEDIROM)
- Twitter ID: @STAR_ZERO
- スライド
- サンプルアプリ
概要
- Android で MVVM を実現するための実装方法
- Model-View-ViewModel でそれぞれ何をやるか
発表内容
- MVVM
-
View
- ViewModel の状態を反映
- 標準の DataBinding では難しい, DataBinding のカスタムセッターを作る
- View の入力を ViewModel へ伝える
- View の入力を DataBinding を使用して、 ViewModel の状態へ反映する
- 双方向バインディング
- ViewModel にイベント処理を移譲
- View で発生したイベントを ViewModel へ処理を移譲
- DataBinding やメソッド呼び出し
- 以下の三つの場合によって異なる
- android:onClick 以外のイベントを処理したい場合: ドキュメントに乗ってないが、定義されているので、ソースコードをみる
- どこにも定義されてないイベントを処理したい場合: setter があるものはそのまま使える
- DataBinding が使えない場合: 直接 ViewModel を呼ぶ
- 以下の三つの場合によって異なる
- ViewModel の状態を反映
-
ViewModel
- Model の処理呼び出し
- ViewModel は、イベントに対する反応と 戻り値のないメソッド の呼び出ししかしない
- View への変更通知イベント
- View を変更する為に ViewModel の状態を変更させて、それを View へ伝える必要がある
- 以下の2ケースがある
- DataBinding を使う場合
- DataBinding が使えない場合 (ダイアログ表示, 画面遷移など): EventBus or RxJava を利用する
- Model の処理呼び出し
-
Model
- View と ViewModel 以外の処理
- ドメイン, ビジネスロジック, DB, API, その他色々
- ViewModel への変更通知イベント
- View と ViewModel 以外の処理
-
講演5: 4年続くアプリにおけるチーム開発
登壇者
概要
- フリル Android 版の軌跡
- フリルにおける開発
- チーム開発をうまく回すためにやってよかったこと
発表内容
-
フリル Android 版の軌跡
- リニューアルを複数回実施
- アプリのアイコンもそれに合わせて変化
- リニューアルに際してのユーザの反応は肯定的なものから否定的なものまで様々だった
- スライドにまとめたのでみてください
- v3.0~ より RxJava+Retrofit 導入して処理の流れをわかりやすくした
- リニューアルを複数回実施
-
フリルにおける開発
- GitHub での運用ルールについて共有
- コーディング規約は Cookpad のものに準拠
- Annotation を積極的に使う
-
@IntRange
: メソッドの引数で、どの範囲の値を許すか -
@Deprecated
: 将来的に非推奨にしたいクラスにつける
-
- プルリクエストがたまる問題
- 溜まってるプルリクエストを Slack に通知するようにした
- QA
- エンジニア、CS スタッフ、デザイナー、プロダクトオーナーが参加
- テストシートを事前にエンジニアが作成し、動作確認する
-
チーム開発をうまく回すためにやってよかったこと
- 委員会制にする
- iOS / Android / サーバー / デザイナー によらず、メンバーは委員会に所属し、
委員会が扱う技術的な課題などを責任を持って対応- → 責任範囲がより明確になる
- 例) Android 6.0 対応, Rails アップデート
- iOS / Android / サーバー / デザイナー によらず、メンバーは委員会に所属し、
- サポート対応の当番制
- Slack の Bot で、CS チームからの質問に答えるエンジニアをランダムで選出する
- 知識の属人化が減った
- Slack の Bot で、CS チームからの質問に答えるエンジニアをランダムで選出する
- リリースノートをエンジニアが書く
- 委員会制にする
講演6: コマンドなしでぼくはAndroid開発できない話
登壇者
- operandoOS さん (Mercari)
- Twitter ID: @operandoOS
- スライド
概要
- Android 開発を効率化するためのコマンドの紹介
発表内容
-
Android でコマンドを使えるようになる為に
- パスを通す
- sdk/tools/
- sdk/tools/bin
- sdk/platform-tools
- sdk/build-tools/
- sdk/platform-tools/systrace
- パスを通す
-
コマンドの紹介
-
adb-peco
-
複数の Android 端末を同時に繋いでいるとき, 単なる adb だと、どっちの端末に対する操作かわからない
-
→ adb-peco なら、実行時に、どっちの端末に対する操作なのか聞いてくれる
$ alias adb='adb peco'
-
過去に実行したコマンドを列挙して、どれを実行するかを聞いてくれるように
拡張したコマンドを別途作成し、gist に公開しました
-
-
input text
- コマンドラインから文字入力ができる
- マルチバイトは入らないので、日本語入力は無理
$ adb shell input text droidkaigi2017
- → 入力カーソルがあるところに "droidkaigi2017" と入る
- コマンドラインから文字入力ができる
-
reboot
- 再起動する
$ adb shell reboot
-
am
-
am hang
- ハングさせる
- reboot させると元に戻る
$ adb am hang
-
-
pm
-
pm path
- 指定したパッケージ名の apk の場所を教えてくれる
$ adb ssshell pm path [packageName]
-
pm clear
- アプリケーションのデータをクリアする
$ adb shell pm clear [packageName]
-
pm list
- インストールされてるパッケージの一覧を取得
-
-
dumpsys
-
システムサービスの状態をダンプ
-
dumpsys activity top
- 現在表示されている Activity の状態をダンプ
$ adb shell dumpsys activity top
- この画面ってどの Activity? Fragment? というときに
-
dumpsys dbinfo [packageInfo]
$ adb shell dumpsys [packageInfo]
-
-
log
- ログを流し込める
$ adb shell log [文字列]
- ここの操作のログが取りたいけど、log.d() したくないとき
- "--ここまでみた--" みたいなのを出したいとき
-
run-as
- 実行するプロセスを変える?
$ adb shell run-as [packageName]
-
dryrun
-
GitHub から clone して実行する
$ gem install dryrun $ dryrun [github のリポジトリ]
-
-
-
紹介したコマンドは GitHub にてまとめられている
講演7: Error Handling in RxJava
登壇者
- yuyakaido さん (エウレカ)
- Twitter ID: @yuyakaido
- スライド
概要
- RxJava におけるエラーハンドリングについて
発表内容
-
RxJava におけるエラーハンドリングは大きく分けて2種類
-
Catch
-
エラー発生時にリカバリーを行う
-
onError, onErrorReturn, onErrorResumeNext
-
onError: エラーがエミットされたら呼び出される
-
onErrorReturn: エラーがエミットされたら代わりのデータを返す
-
onErrorResumeNext: エラーがエミットされたら代わりの Observable を返す
-
-
Retry
-
エラー発生時に resubscribe する
-
retry, retryWhen
-
retry: エラーがエミットされたら resubscribe する。引数で回数を指定可能
-
retryWhen: resubscribe する条件を細かく制御できる
-
-
-
基本編
- エラーをトーストで表示する
- onError() のとこに Toast で表示させる
- 代わりのデータを表示する
- onErrorReturn() のとこに、エラーが発生したときに、代わりに表示させる値を返す処理を書く
- エラーの種類で処理を分岐する
- サーバが返したエラーコードによって処理を分ける場合など使う
- onErrorResumeNext() にて、引数の Throwable の値に応じて異なる処理を実装すると良い
- リトライする
- retry()
- リトライするかどうかをユーザに尋ねる
- Observable にエラーが流れたら Snackbar を表示し、ユーザがリトライを押した場合に resubscribe する
- エラーをトーストで表示する
-
応用編
- リトライを三回行い、ダメだったらキャッシュを表示する
- retry()、 onErrorReturn() を組み合わせる
- リトライを三回行い、ダメだったらキャッシュを表示し、キャッシュがない場合はトーストを表示する
- retry()、onErrorResumeNext() を組み合わせる
- リトライを三回行い、ダメだったらキャッシュを表示する
-
実践編
- Retrofit のエラーハンドリング
- 全ての WebAPI 呼び出し処理に入れ込むのに、コピペしていくのは大変なので、共通処理をクラスに切り出して、 それを全 API の処理に入れ込む
- → RxJavaCallAdapterFactory を拡張する
- 全ての WebAPI 呼び出し処理に入れ込むのに、コピペしていくのは大変なので、共通処理をクラスに切り出して、 それを全 API の処理に入れ込む
- Retrofit のエラーハンドリング
質疑応答
- 複数の API 呼び出しして、その結果をマージして表示するという状況で、一つはうまく取得できて、もう一つは取得できなかった、という場合にどのように処理している?
- → 各レイヤーにてエラー処理を挟んで対応する
講演8: テスト0から目指すクラッシュフリー率99%
登壇者
- fushiroyamaさん (日本経済新聞)
- Twitter ID: @fushiroyama
- スライド
概要
- クラッシュ率を抑えるためのライブラリ/コード設計の紹介
発表内容
-
Crashlytics
-
Crashlytics # logException(e) で non-fatal なログを送れる
-
Crashlytics にログ情報を送れます
- Crashlytics.setUserIdentifier()
- Crashlytics.setUserEmail()
- Crashlytics.setUserName()
- そのまま送るのは問題なので、エンコードしましょう
-
-
Firebase Crash Reporting
- Contextual History がすごく便利
- どんな状況で落ちたのかがわかる
- Crashlytics 使っている人も Firebase Crash Reporting 使ってみましょう
- Contextual History がすごく便利
-
Timber
- Timber.Tree を継承して、 log メソッドを継承し、
Crashlytics.logException(), FirebaseCrash.report() をすると、
Timber.e とかやったときにその内容が、Crashlytics や Firebase に送信される
- Timber.Tree を継承して、 log メソッドを継承し、
-
テストがかけない理由
- Activity が大きすぎるため
- 例えば、Activity # fetchUser() というのがあって、それを呼んでいる処理のテストをしたい、としたとき
- そのままだとテストは書けない
- → fetchUser() を Activity から切り離して、呼び出すためのクラスを作成する
- ApiClient も Activity のフィールドに直接アクセスせず、クラスにフィールドを作るようにする
-
Activity の責務を他のクラスに切り出す
- MVP パターンを使う
-
クラッシュの多くの理由は java.lang.IllegalStateException Can not perform this action after onSaveInstanceState
- ネットワークの遅延とかで、 Fragment の更新が呼ばれてはいけないタイミングで呼ばれたりするのが原因
- FragmentTransaction # commit
- DialogFragment # show
- 非同期通信の結果を DialogFragment # show とかするのは危険
- RxLifecycle を利用すればいいけど、レガシーな開発現場には必要のない機能も含まれる
- → Vulture を開発しました。Activity/Fragment のライフサイクル中で非同期なコールバックを扱えます
- ネットワークの遅延とかで、 Fragment の更新が呼ばれてはいけないタイミングで呼ばれたりするのが原因