アプリ内の独自マージンは悪

最近アプリを1本リリースしたが、色々と思うことがあった。 その一つが独自マージンは悪だということ。

作っていたアプリはゲームエンジンは使ってないものの、ツール系アプリと違って楽しむ系のゲームに近いジャンルだった。 そこで、担当デザイナーは5dp刻みのマージンを指定した。

1つ目の問題〜UIコンポーネント

5dp刻みのマージンはこちらで指定したUIであれば問題なく機能した。 ただし、プラットフォームのUIコンポーネントを多用するような画面では邪魔になった。 例えば設定画面や、検索画面といったアプリではなく、プラットフォームのデザインに寄せた部分だ。 UIコンポーネントは4dpで設計されていて、こちらのUIは5dpで設計されている。 画面内はもちろん、画面遷移で1dpの差が出てしまう。

2つ目の問題〜他デザイナーの参画〜

途中で当初居たデザイナーのタスクが溢れてしまい、途中で別のデザイナーがプロジェクトに参加した。 そのデザイナーはマテリアルデザインに慣れているため、とても良いことに思えた。 ただし、実際に渡されたデザインのマージンは4dp刻みだった。 デザイナー間の意思疎通が取れてなかった。

結局どうしたか

細かいマージンの差分吸収はエンジニアが行った。 新規デザイナーが上げてくる4dpは5dpに変換した。 ちなみに初回から参加していたデザイナーも必ず5dpに指定したわけじゃなく、4dp刻みになっていたこともあった。

まとめ

結局5dpに指定したことが全ての問題だったと思う。素直に4dp刻みにすれば皆余計なタスクは発生しなかった。 既存のUIコンポーネントは4dp刻みで設計されている。そちらに合わせるべき。 AndroidStudioのConstraintLayoutUIデザイナも8dp(だったか)など2の倍数で指定するようになっていたはず。 XCodeも2の倍数が基本。2の倍数指定にするべき。

Boolean変数/関数にisプレフィックスを付けるのはNG

言語問わずこういったコーディング規約の話は良く議論されるが、担当中アプリでもこの話が出てきた。
Boolean変数/関数にisプレフィックスを付けるのはOKかNGか。
結論から言うとプレフィックスは付けてはいけない。

isはプレフィックスではなく英文としておかしくない時だけ付ける

変数に名前を付けるとき、考えるのはプレフィックスではなく英文としておかしくない命名にするべき。

  • isAnsweredQuestion → be動詞 + 動詞で動詞が被っているのでおかしい
  • answeredQuestion → 動詞 + 名詞なのでOK
  • isEmpty → be動詞 + 形容詞なのでOK

言語が提供するAPIにも付いてない

JavaのFile.exists()もisExists()ではなくexists()とプレフィックスはついてない。
Object.equals()もだ。

そもそもプレフィックスはなぜ付けてた

IDEが発達していなかった時の産物。現在のIDEではむしろコードのノイズになる。

まとめ

  • プレフィックスは過去の産物。できるだけ使わないことを意識する。
  • 命名するときは英語として違和感ない形のほうが本質を捉えた命名にできる。→理解しやすくバグの少ないコードに繋がる。
  • 英語勉強しよう。(私も。英語間違ってたらごめん。)

Androidで通知をタップしたかどうか検知する

Notification経由でActivityが起動されたか検知するというもの。 以前作っていたアプリではActivity.onNewIntent()でやっていた記憶があったが実装しただけでは通知されなかった。

ググったところ↓の記事を見つけた。onNewIntent()で受け取りたいActivityにandroid:launchMode="singleTop"を設定しろ。というもの。 stackoverflow.com

singleTopを設定しない場合、Activityスタックに同じActivityが複数残っていたので、NotificationのIntentがnewIntent扱いにならない?のかな。 この辺忘れやすいので覚えておきたいところ。

FirebaseのクラッシュレポートがCrashlyticsに移行するらしい

Firebaseのダッシュボードみてたらこんなメッセージが表示されていた。
「将来は、Crashlytics が Firebase のクラッシュ レポート機能になります。今すぐお試しください。FABRIC でインストール」 f:id:paraya3636:20170526124215p:plain

マジ?正直逆だと思ってた。FabricがGoogleに買収されていたのは知っていたので、将来的にはFirebaseに統合されるんだろうなと思っていたし動こうとしていた。 Firebaseクラッシュレポートが今すぐ廃止になるという話ではないようだけど…結構驚いた。

紹介ページにもその旨書かれていた。重大なバグがないならCrashlyticsに移行するのを推奨する的な感じかな。 f:id:paraya3636:20170526124234p:plain https://firebase.google.com/docs/crash/

今後もCrashlyticsを使っていこう。

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など描画イベントの波及を増やしてしまうんだろうか?マイナスマージンは避けようと思った。