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

GoogleI/O 2017でAndroidがKotlinを正式サポート声明出してたけど恐れるものではない

つい先日行われたGoogleI/O 2017で、AndroidがKotlinを公式にサポートする声明を出していた。 具体的にはAndroidStudio3.0から何もせず使えるということなので、3.0からKotlinプラグインが含まれる形で配布されると推測。

その関連でQiitaではKotlin入門の記事が急激にいいね付いてたり、TwitterではKotlinに対する不安をちらほら見かけた。 新しい言語をサポートするということで驚いているエンジニアが多い印象。 でもKotlinを使ってる身からするとそんなに身構えなくてもいいのになという感想を持ったので、少しKotlinがどんなものなのかを振り返ってみる。

KotlinはJava

Twitter上でKotlinが導入されたらJavaは使えなくなる?的なツイートを見たけど、そんなことはない。 KotlinとJavaは共存できるのでどちらか片方を選んだらもう片方は使えなくなるといったことはない。 むしろKotlinはビルド時にJavaのコードに変換される。 DroidKaigi2017でトクバイの八木さんが講演していたので参考までに。

speakerdeck.com

JavaでもKotlinと同等のコードは書ける。ただめんどい。

上記で説明したとおりKotlinはJavaに変換される。つまり逆の話をするとJavaでもKotlinのコードに相当するものを書ける。 でもコード量が多くなりがちで面倒。例えば以下のJavaUserクラスとKotlinUserクラスはビルド時も実行時も同じ挙動をするがコード量がだいぶ増減している。 概ねfinal宣言の簡略化とgetter/setterの自動生成、プライマリコンストラクタのお陰でKotlinはコード削減している。

public class JavaUser {
    private final String userId;

    public JavaUser(@NotNull String userId) {
        this.userId = userId;
    }

    public String getUserId() {
        return userId;
    }
}
class KotlinUser(val userId: String)

JavaっぽくKotlinを書いてもいい

Kotlinは言語なのでJavaと記法が違うところがある。特に上のコードを見てプライマリコンストラクタってなんだよ…って思う人も居ると思う。 初期の頃私も思ってたし、書き方合わないと思ってた。 そういう所はJavaっぽく書いても良いと思う。 例えばプライマリコンストラクタはセカンダリコンストラクタを用いることでJavaっぽく書ける。

class KotlinUser {

    val userId: String
    
    constructor(userId: String) {
        this.userId = userId
    }
}

コード量は増えるもののJavaに慣れてるなら上記コードでもいいと思う。 Kotlinの本質は、NullPointerExceptionの激減finalフィールドの書きやすさあたりだと思っているので本質のメリットさえ享受できていれば慣れるまでJavaぽく書いてもいいと思う。

Javaと共存

最初のほうでも書いたがKotlinはJavaなのでJavaと共存可能。 KotlinからJavaのコードを呼べるし、JavaからKotlinのコードを呼べる。 SwiftのようなBridging-Header.hなど設定ファイルは必要ない。

むしろJavaで書いてもいい

Kotlinの一部記法が良くわからず、一部Javaで書きたい人も居ると思う。 Java書いてもいいと思う。100%Kotlinにする必要はないと思う。

そして、Kotlinを勉強してみてもJavaがやっぱりいいと思う人は、Javaで書いてもいいと思う。 エンジニアにも好みがあるし、一番速く一番品質良く書ける言語で書くのが一番だと思う。

やっぱりKotlinをおすすめしたい

上でJavaで書いてもいいとしたが、でもKotlinで書けるならKotlinで書いたほうが良いとは思う。 最近仕事でJava100%で書かれたAndroidアプリを引き継いだのだが、NullPointerExceptionでのクラッシュレポートが多いので、Kotlin導入出来ていれば大部分は防げたんじゃないかという思いが強くある。 Kotlinは強くおすすめしたい。

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からかな?