読者です 読者をやめる 読者になる 読者になる

AndroidStudioのWebpコンバータはとても良い

AndroidStudio2.3からwebpコンバータが搭載された。

画像を右クリック→ convert to WebP でコンバート可能。 早速つかってみた。

適用した箇所

写真のような画像は40%以上ファイルサイズが小さくなった。 逆にアイコン画像などは大きく変化しなかったり逆にファイルサイズが大きくなったりした。

75%品質で画像サイズが大きくなってしまう場合

品質を100にすると1〜2%ほどだが元画像から若干ファイルサイズを削ることができる。

Android5系でwebpが正しく表示されないことがある

画像内に透過があると画像の一部が壊れたように表示されることがあった。 透過画像でも一部の画像のみだったので、一旦pngに戻して対応した。

pngへのコンバートも出来る

png→wep→pngすると元の画像より大きくなってしまった。 pngに戻したいときは元画像に戻したほうが良い。

まとめ

少し癖はあるがアプリサイズを小さく出来るのでとても良いと思った。 特に今作ってるアプリはサーバレスでアプリサイズが肥大化してるので活用したい。

Android5系の一部端末でアニメーションがカクつくのを修正した

ARROWS NX/XperiaZ3/XperiaZ4辺りのAndroid5系端末でアニメーションがガクガクになっていたのを修正した。 Nexus5ではそこまでカクついたりはしていなかった。

問題箇所

問題箇所は以下が主な原因だった。

  • 余分なViewGroupなどレイアウトxmlの階層構造の深さ
  • android:clipChildren="false"
  • android:clipToPadding="false"

やっていたアニメーション

細かくは話せないがそこそこ複雑なViewで以下のようなアニメーションを同時に行っていた。

  • 背景の大きい画像をフェードアウト
  • CardViewをフェードしつつ45度回転しつつ画面外に移動させて見えなくする
  • CardView内の内容を更新
  • 背景の大きい画像を更新
  • CardViewをフェードしつつ45度回転しつつ画面内に登場させる
  • 背景の大きい画像をフェードイン

FPSを計測する

Taktを利用した。

github.com

FPSは通常30〜50 アニメーションする際に15まで落ちる感じ。 修正後は40〜50FPSで安定するようになった。

感想

  • 調査段階ではこの辺を読んで、アニメーションとViewの更新処理をより厳密に分けようとOnGlobalLayoutListenerを使ってみたりしたけどあまり効果はなかった。
  • 余分なViewGroupが多かったので、よりフラットな構造にしたら目に見えて改善したので驚いた。
  • clipChildren/clipToPaddingも変更しただけで目に見えて改善した。onLayoutなど描画イベントの波及を増やしてしまうんだろうか?マイナスマージンは避けようと思った。

DDDでの失敗談

近々同僚とLT大会をするので話すことをMarkdownで書いてみたので投稿。

DDDでの失敗談

DDDを1年半くらい実践してきて色々成功したこと失敗したことがあった。 この場では失敗談を共有してアンチパターンへの対処法をお伝えしたい。

ドメインしっかりと設計すれば変更少なくてすむだろう→そんなことはなかった

ドメインの設計見直しタイミングは以下のケースがあった。

  • 仕様を固めていく上で他の仕様も変わるケース
  • ユーザテストの意見反映で仕様が変わるケース
  • 実装を進めていてより良い設計を思いついたケース

意外とコロコロ変わる。 悩んでベスト設計を目指すのではなく、ベター設計で速度を優先したほうが良い。

ユビキタス言語決めたら変更はないだろう→変更入りまくり

  • 名前が学術名で難しいので分かりやすい名前に変えた
  • デザイン進めて行く上で変わった
  • 他アプリでの名称違うからそっちの反映
  • 監修担当から変更要望反映

ユビキタス言語は変わる。 変わらないように努めるべきだが変わる可能性は充分にあることを留意する。

通信も結局はデータ操作だからData層に入れられそう→Domain層でHttpステータスに応じて色々したい…

  • 通信処理もDB操作と同じくset/getのイメージで、returnもValue/nullで扱えそう。通信結果はData層内で隠蔽できそうだと思った。
  • でもHttpステータスに応じてアクション変えたいね。
  • やっぱりドメイン層で扱いたい。

通信結果もビジネスロジックとしてDomain層で扱いたい要望はあるので隠蔽しすぎないこと。 個人的にはCleanArchitectureの図のように、Data層/Web層は分離しとくと分かりやすくて良いと思う。

ビジネスロジックどこ書けばいいかわからん…とりあえず専用クラス作ってそこにメソッド書くか→Entityがただのデータの入れ物化

ビジネスロジックを書く際は既存のEntity/ValueObjectで担当できるか検討する。 担当出来ない場合、サービスクラスとして実装する。UseCaseには書かない。

まとめ

  • ドメインはアプリを作っていく過程で変わる可能性は充分にあるし、後日より良い設計を思いつく可能性が高い。設計に迷ったら時間を掛けすぎず一旦思いついた限りの内容で実装進める。
  • ユビキタス言語はプロジェクト初期に確定するのは難しい。変わらないように努力しつつ、後々変わる事も意識しておく。
  • ドメインは通信結果も意識する。Data層に押し込めすぎないようにする。
  • 振る舞いを考えるときは既存のEntitiy、ValueObjectで探すこと。安易にロジックをUseCaseなりに押し込めない。

Kotlinの単一式関数は責務を制約する時に便利

単一式関数とは、通常のreturnで返却する式↓を

fun method(type: Type) : Int {
  return when(type) {
    Type.TypeA -> 0
    Type.TypeB -> 1
  }
}

代入式で省いて書ける↓

fun method(type: Type) = when(type) {
  Type.TypeA -> 0
  Type.TypeB -> 1
}

といったもの。英語ではSingle-Expression functions。 kotlinlang.org

この書き方で何が変わるんだ、シンプルになるとは言いつつもそこまで変わってない。returnしてもいいじゃないかと思っていたけど一つ使い所を思いついた。 余分な処理を挟んでほしくない箇所に制限を掛ける、例えばMapper。

余分な処理を挟んでほしくない場合にすごい便利

Mapperでは↓のように実装すると思う。名前の通り責務は変換するだけ。

fun map(type: Type) : OtherType {
  return OtherType(type.name)
}

↑このコードで問題なのは、returnの前に何か処理が書けるということ。 引数のtypeではなく、DB内のtypeを読み込んでOtherTypeにmapする。と言ったことが出来てしまう。

fun map(type: Type) = OtherType(type.name)

↑単一式によって処理を挟める隙間が消えた。コードを挟むには単一式を変更して中括弧が必要になる。

あんまり変わってない?

言うほど確かに変わってない。でも小さな制約だけどコードレビュー時に役に立つと思う。 単一式では責務が限定的で分かりやすい。単一式から通常の関数に書き換えられたということは注意するコードの可能性があると思う。

引数のtypeではなく、DB内のtypeを読み込んでOtherTypeにmapするなんてする?

実際にそういうコードを見てしまったので今回の案を思いついた。 責務を限定してエラーで表現してくれるのは良いと思う。

Android開発者はそろそろ32GBメモリ以上のPCに移行を考える時期

最近AndroidStudioのビルド速度の遅さと補完時にフリーズするのに耐えられず、会社のPCを買い替えてもらった。 メモリ8GBの2012年頃MBP→CPU4Ghzメモリ32GBのiMacへ。 買い替えの際になぜMBPではなくiMacを買ったのか、その経緯を書く。 結論から言うと現在のiMac上でのメモリ使用率は20〜22/32GBであり、メモリ32GBほどのマシンを買ったほうがよい。 Macの話だけどWindowsでも同様だと思う。

AndroidStudioの推奨スペック

AndroidStudioの推奨スペックは公式サイトには以下の記述がある 3 GB 以上の RAM、8 GB を推奨、さらに Android エミュレータ用に 1 GB

では推奨スペックで業務利用に耐えられるのか?全くもってそんなことはない。

8GBはかなり辛い

AndroidStudioの推奨スペック以上には達しており、小規模アプリの初期のコード量が少ない場合は問題ない。 ただし、開発後期に進むに連れて厳しさが目立ってくる。 具体的に言うとレイアウトXMLの補完に10秒ほどかかったり。 アプリ実行に2分30秒かかったり。 メモリ使用率は12/8GBで超えた分はストレージをスワップメモリとして使用していた。

16GBはギリギリ?

16GBMBP使ってる同僚の話だとそこまで困ってはいないようだ。まだ使えるレベルなんだと思う。 現状のMBPが積める最大サイズ。

32GBは快適

iMacとMBPを直接比較するのはどうなんだというのもあるが、やっぱり32GBiMacは快適。 ビルド時間は2分30秒→9秒へ。View周りの凝った実装するにはビルド→実機転送を繰り返すのでこれくらいの速度は欲しい。 補完でフリーズすることもない。

iOSはどうなのか?

iOS開発者のiMacアクティビティモニタを見たら同じく20GB/32GB程度だった。

まとめ

CPU性能も関係しているし具体的な数値ベースで調査を行ったわけではないけど、私と同僚のMBPで比較しても快適さが違うのでメモリの差が大きく関係してそうだなぁという肌感の印象。 MBPにも近々32GB積めるようになるらしいのでそれを買うのもいいかもしれない。話はすこし逸れるがディープラーニングもしたいならCUDAの関係でNVIDIA GPUが搭載されるようになるのを待つのもありかも。 PCは高い金額なので買うなら良いPCを買おう。

16GBを使ってる人→まだ使える。
これからPC買う人→32GBPC買おう。
8GBを使ってる人→32GBPCを買おう。

Nexus6PのBootloaderでFAILED (remote: oem unlock is not allowed)

Android O Previewも出たことだしNexus6Pに入れてみようとしたのだけどfastboot flashing unlockで見慣れないエラーが出た。

$ fastboot flashing unlock
FAILED (remote: oem unlock is not allowed)

調べてみるとこんなサイトが見つかった 要約するとアプリ起動中に開発者向けオプションから設定する必要があるらしい。

設定アプリ->開発者向けオプション->OEMロック解除ON

↑設定後、fastboot flashing unlockが実行出来てAndroid O Previewの書き込みすることができた。 以前はこのオプション無かったと思うんだよなーAndroidNからかな?

AndroidStudioのプレビュー表示にView.isInEditModeが便利

AndroidStudioのプレビュー表示は便利だけど、稀に良い感じに表示してくれない時がある。例えばカスタムViewの初期化時にレイアウトパラムを変更したときだ。xml上のtools:visibilityなどを使いたくなるが、コード上なので使えないケースがある。 そういった時にView.isInEditModeをつかうと便利。 プレビューではisInEditMode == trueが適用されているので、if (isInEditMode) { return }などでプレビュー時には該当の初期化処理をしないなどすればよい感じにプレビュー表示してくれる。

経緯

私のケースでは全てのButtonのアニメーションを一括で変更したかった。 しかしスケールアップアニメーションを正しく表示するのにclicpChildren/clipToPaddingを全てのレイアウトで適用するのが面倒だったので、Viewから親のViewGroupのclicpChildren/clipToPaddingを全て書き換えるということを行った。(これはこれでどうなのかという話もあるが)

つい先日のDroidKaigi2017で発表があったSmoke and Mirrors in Androidでも同様の処理をしていたので真似をしてみたのだ。

github.com

しかし、clicpChildren/clipToPaddingを書き換えるとプレビュー上のViewが正しく表示されなくなった。 実行時は問題ないのだけどプレビュー時に正しく表示されない。 そこで if (isInEditMode) { return } を利用した。