AsyncLayoutInflater vs Anko, AnkoはLayoutXMLを殺すのか?

YokomakuさんがAsyncLayoutInflaterLithoのパフォーマンス比較を行っていました。 keithyokoma.hatenablog.com

ちょっと気になったので私もAsyncLayoutInflaterAnkoの比較を行ってみました。

AsyncLayoutInflater? Anko?

AsyncLayoutInflater?

LayoutXMLのInflateを非同期で行います。

Anko?

Kotlin製のView DSLです。コンセプトはいくかありますが以下のような利点が。

  • LayoutXMLのパースを行わないため高速
  • コードでViewを組むのでTypeSafe

以前軽く記事にしています。 lvla.hatenablog.com

Sample Codes

サンプルコードはYokomakuさんのものをfalkさせていただきました。 github.com

AsyncLayoutInflater

<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/scroll"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_below="@+id/text"
    tools:context="io.github.keithyokoma.asynclayout.MainActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <!-- 10000 TextViews -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hello World!"/>
        ...
</LinearLayout>
</ScrollView>
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val s = System.nanoTime()
        AsyncLayoutInflater(this).inflate(R.layout.activity_main, null) { view, _, _ ->
            setContentView(view)
            val e = System.nanoTime()
            Log.i("TIME", "${(e-s)} nano sec")
        }
    }
}

Anko

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    measureNanoTime {
      val text = "Hello World!"
      scrollView {
        lparams(width = matchParent, height = matchParent)
        verticalLayout {
          for (i in 0..9999) {
            textView(text)
          }
        }.lparams(width = matchParent, height = wrapContent)
      }
    }.let { Log.i("TIME", "$it nano sec") }
  }
}

Benchmark

AsyncLayoutInflater

  • 2268048923 ns
  • 2274749393 ns
  • 2299624654 ns
  • 2346767994 ns
  • 2387729873 ns

Ave. 2315384167.4 ns (2315.38 ms)

Anko

  • 1692746575 ns
  • 1719153036 ns
  • 1676609542 ns
  • 1737803819 ns
  • 1757240852 ns

Ave. 1716710764.8 ns (1716.71 ms)

AsyncLayoutInflater vs Anko

やはりXMLのパースを行わない分、Ankoのほうが速いです。
じゃぁAnkoの方がいいかというとそうでもないかなーと。もしかしたら(Ankoに比べたら)ちょっと遅いけどUI Threadを止めないAsyncLayoutInflaterのほうが良いケースもあるかもしれません。
そして、AnkoではXMLを書かないので当然Databindingを利用できません。

CA.ktでJavaに無い機能をKotlinがどう実現してるのか話してきました。

どうもどうも。

6/15にCyberAgentで開催したCA.kt #1で発表しました。

様子

How Kotlin implements features Java doesn’t have

speakerdeck.com 今回は以下の3つに関して話しました。

  • NonNull/Nullable
  • Extension Function
  • Named Argument/Default Argument

ブログ向けに改めて書き起こします。サンプルコードはKotlinとそのBytecodeをデコンパイルして生成したJavaです。

NonNull/Nullable

関数の引数を例に解説しました。この場合関数がpublicなのかprivateなのか引数がJavaでいうPrimitive型なのかClass型なのかでコンパイル後のBytecodeが行っている処理に変化が置きます。

引数がNonNull-Primitive型

fun nonNullInt(number: Int) {
  number.toString()
}
public final void nonNullInt(int number) {
   String.valueOf(number);
}

これらはもともとnullを許容しないのでそのままコンパイルされます。

  • byte
  • short
  • int
  • long
  • char
  • float
  • double
  • boolean

引数がNullable-Primitive型

fun nullableInt(number: Int?) {
  number?.toString()
}
public final void nullableInt(@Nullable Integer number) {
   if(number != null) {
      String.valueOf(number.intValue());
   }
}

nullableの場合、intのBoxTypeであるIntegerにへコンパイルされます。Integerであればnullを許容するからです。
number?.toString()といったSafe Callはif文でnullチェックを行って実行されます。

引数がNonNull-Class型

fun nonNullString(string: String) {
  string.length
}
public final void nonNullString(@NotNull String string) {
   Intrinsics.checkParameterIsNotNull(string, "string");
   string.length();
}

StringなどのClass型は本来nullを許容する型なので、関数の最初にIntrinsics#checkParameterIsNotNull(Kotlinのruntimeが持っている関数)で引数のnullチェックを行います。JavaなどのNullable/NonNullの区別がない言語からnullを渡される可能性があるからです。

引数がNullable-Class型

private fun nullableString(string: String?) {
  string?.length
}
private final void nullableString(String string) {
   if(string != null) {
      string.length();
   }
}

今度は関数先頭でのIntrinsics.checkParameterIsNotNullの呼び出しがなくなりました。引数のnullのチェックをする必要が無いからです。

Private関数

引数がNonNull-Class型の項でJavaからnullが渡される可能性を考えて、nullチェックを行っていると解説しました。
しかし、この関数をPrivateにするとまた変化が起こります。

private fun nonNullString(string: String) {
  string.length
}
private final void nonNullString(String string) {
   string.length();
}

引数に対する@NotNullアノテーションIntrinsics.checkParameterIsNotNullの呼び出しがなくなりました。Private関数であればKotlin以外の言語から呼び出されない事が保証されるからです(Reflectionは例外ですが)。

Extension Function

Package Level Extension Function

StringExtension.ktファイルの直下に以下のような拡張関数を実装してみます。

fun String.println() {
    println(this)
}
public final class StringExtensionKt {
   public static final void println(@NotNull String $receiver) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      System.out.println($receiver);
   }
}

FileNameKtクラスのstatic methodとしてコンパイルされます。引数は拡張対象です。
そして、このExtension Functionの呼び出し元はこのstatic methodを呼ぶようにコンパイルされます。

Class Level Extension Function

class Foo {
  fun String.println() {
    println(this)
  }
}
public final class Foo {
  public final void println(@NotNull String $receiver) {
  Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
  System.out.println($receiver);
  }
}

この場合はFooクラスのmethodとしてコンパイルされます。

Private Package Level Extension Function

Private Package Level Extension Functionは同ファイル内のみからアクセス可能なExtension Functionです。もちろんこちらもPackage Levelなのでstatic methodとしてコンパイルされます。しかし、そのstatic methodの公開範囲はprivateです。

以下のようなコードをFoo.ktに実装した場合について考えてみましょう。

private fun Foo.doSomething() {
}

class Foo {
  init {
    doSomething()
  }
}

Foo#doSomethingはPrivate Package Level Extension Functionなので、FooKtクラスprivate static methodとして実装されます。 FooKtのprivateなメソッドにFooからアクセスできるのはおかしいですね。
そこで、Bytecodeを見てみると以下のようになっています。

public final static synthetic access$doSomething(LFoo;)V
    @Lorg/jetbrains/annotations/NotNull;() // invisible, parameter 0
   L0
    LINENUMBER 1 L0
    ALOAD 0
    INVOKESTATIC FooKt.doSomething (LFoo;)V
    RETURN
   L1
    LOCALVARIABLE $receiver LFoo; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1
  ...

Fooを引数とするpublic final static synthetic access$doSomethingというsynthetic methodが生成されています。 これはコンパイラが自動生成したメソッドです。これを呼び出し元をこのメソッドを経由するようにすることでFooからFooKtのprivate static methodにアクセスします。

余談ですが、synthethic methodJavaNested Classを実装したときにも生成されます。Outer classInner class間でprivateなメソッドまたはフィールドにアクセスしようとした時です。JavaにはNested Classの仕様がありますが、Bytecode上は別々のクラスとしてコンパイルされるからです。

Named Argument

fun foo(one: String, two: String, three: String) {
}

foo(three = "tres", two = "dos", one = "uno")
String var1 = "uno";
String var2 = "dos";
String var3 = "tres";
this.foo(var1, var2, var3);

Named Argumentは一度変数に代入してから順番を揃えて関数を呼び出すようにコンパイルされます。

Default Argument

fun foo(one: String = “one",
        two: String = “two",
        three: String = "three") {
}

foo(two = "dos")
// staticなのはfoo関数をパッケージレベルに実装してコンパイルしたため。
public static void foo$default(
    String var1, String var2, String var3, int var4, Object var5) {
   if((var4 & 1) != 0) {
      var1 = "one";
   }
   if((var4 & 2) != 0) {
      var2 = "two";
   }
   if((var4 & 4) != 0) {
      var3 = "three";
   }
   foo(var1, var2, var3);
}

foo$default((String)null, "dos", (String)null, 5, (Object)null);

Default ArgumentfunctionName$defaultというメソッドが生成され、その中から関数を呼ぶようにコンパイルされます。 この例では、第1引数〜第3引数は元の関数と一致し、第4引数はDefault Argumentを適用するべき位置をこの$defaultメソッドに教えるための設定値、第5引数のObjectは用途不明です(誰か教えてくれ)。
Default Argumentを適用すべき箇所を1すべきでない箇所を0と表現します。
すると、foo$default((String)null, "dos", (String)null, 5, (Object)null)1, 0, 1となります。101を10進数に治すと5になります。これを第4引数に代入しています。 $defaultメソッドの中では101001, 010, 100論理積0と比較することでDefault Argumentを実際に適用するかどうかを決定しています。

こういった知識は必要なのか

上記はほんの一部に過ぎないが、KotlinはこのようにしてJavaが持たない機能を実現している。私達がよくある無駄なコードを書く手間をコンパイラが肩代わりしてくれています。
この知識が必要かといわれると、基本的には必要ないと思う。しかし、Javaとの相互運用を行う際にKotlinのコードがJavaからどうみえるのかといったことが分かると無駄にハマることが減ると思う。
たとえば、AndroidDataBindingBindingAdapterstatic methodを利用する。これをそのままKotlinでcompanion objectで実装しようとしてもうまくいかない。companion objectはそのクラスのinner class Companionとしてコンパイルされ、そのクラスのstatic fieldとなるからです。
そこで、Javaからどうみえるかを意識できていれば「@JvmStaticアノテーションを使おう」だとか「Extension Functionとして実装しよう」といったことが思いつきます。

SoftBankのミスで信用情報ブラック認定されてしまってるっぽい。

f:id:lvla:20170531230227p:plain

tl;dr

  • 6年前にiPhone4を分割購入してる。
  • MySoftBankを見ると完済したことになってる。
  • 信用情報記録にはiPhone4の代金として1000円が未払いということが、毎月記録されていた。
  • しょうがねーからメールで問い合わせてる。

全然クレジットカードが作れない。

やらしい話になるが同世代の平均収入よりもそこそこ多い額をもらっているはずだが、楽天カードですら落ちる
3回くらい落ちてる(ちゃんと期間を置いて申請してるよ)。
学生の頃に作った上限額の低い銀行系カードの上限額引き上げもできない。

おかしい。

信用情報記録開示を行った

しょうがないのでCICとJICCで信用情報記録をした。

すると、2011年5月11日にソフトバンクと契約した4.6万円24回払いのiPhone4(当時流行りの月月割で実質0円という奴)の残り1000円の支払いが2013年8月21日から滞っていると記録されていた。

えっ。約4年間も支払いを踏み倒しているんですか!!
そりゃぁブラック認定でクレジットカードの審査通らないわけだよ。

でもそれはおかしい。

完済してる。 f:id:lvla:20170531223013p:plain
MySoftBankで割賦契約内容を照会した。

そりゃそうだよなー。完済してなかったらその後iPhone5iPhone6の分割払いを却下されるはずだ。
督促も来てないし、そもそも未払いがあれば引き落とされるはず。

ただ、気になるのはこのページにてiPhone5iPhone6に関する記載が無いこと。

  • もしかしたら正常に支払い処理が完了している契約はここに表示されないっていう仕様で、iPhone4のみが支払いは正常に完了しているがなんかのフラグがtrueになったままのためこのページに表示されてるんじゃないか?
  • 引落しや未払いの督促、信用情報の記録を行うバッチ処理で参照するテーブルが別々なため矛盾が生じてるんじゃないか?

とかいろいろ想像できる。それは辛い。データに不整合が起きないようにデータベースを改善せねば。

プレスリリース出してた

www.softbank.jp
(2013年の情報)

分割支払金等をご入金いただいたにもかかわらず、未入金として信用情報機関※1に登録してしまうという事象が発生しておりました。
(略)
現時点において、誤登録された情報は、全て正しい信用情報へと修正されております。
影響が生じた可能性のある件数は、16,827件となります。
(略)
影響があったかもしれないお客さまへのお詫びとご連絡をさせていただきました。

連絡はもらっていないし、現在もたった1000円ぽっちを4年間踏み倒していると演出されつづけているんですが…。

メールした。

これでは一生ブラックリスト入りになってしまう。それは不便。
クレジットカードつくれないし家も車もローン組めない(両方欲しくないけど)。

経緯の説明と修正の依頼をメールした。
なんか続報あったら書くかもしれないし、書かないかも。

ついにGoogleがAndroidKotlin公式サポート👍🏻

blog.jetbrains.com

本日のGoogle I/O 2017にてGoogleAndroid開発言語としてKotlinを公式サポートすることを発表しました。 First class language、なんていい響きなんでしょう。

youtu.be

Stephanieの発表の中で突然の「One more thing…」
すると見たことのある形がパターンロックとして出現し…
f:id:lvla:20170518063159p:plain
Kotlinのロゴが!!
f:id:lvla:20170518063208p:plain
沸く会場
f:id:lvla:20170518063353p:plain
JetBrains CEOやAndrey Breslavが映し出されました。
f:id:lvla:20170518063615p:plain

その後のDeveloper向けKeynoteでもKotlinはたくさん登場しました(現在進行系で登場している)。
youtu.be

既存コードとの共存が可能であることや
f:id:lvla:20170518064153p:plain
機能の紹介
f:id:lvla:20170518064139p:plain
すでにプロダクションで利用されていることなどが話されました。
f:id:lvla:20170518064246p:plain

JetBrainsのAndrey BreslavとHadi Hariri、みんな大好きJake Wharton、PinterestのChristina LeeによるKotlinセッションの告知もされていました。
f:id:lvla:20170518064542p:plain

CyberAgentに入社しました。

2017年3月末でEurekaを退職し、4月にCyberAgentに入社しました。

誰?

前職

10ヶ月間、EurekaでPairsというデーティングサービスのAndroidアプリ開発をやってました。
ORMの移行とかアーキテクチャの刷新、新機能開発なんかをやってました。そこら辺に関しては適当にこのブログに書いてあるので興味があれば読んでみてください。

4, 5年前からあるアプリということできついコードに溢れていましたが、改善しようともがくことで色々学ぶことができました。また、大好きなKotlinの導入を受け入れてくれたチームにも感謝しています。

転職

1年も経たずにやめてしまったのでやばいことでもあったのかと聞かれます。
組織への不満は多少ありましたが、私には致命的な出来事はありませんでした。私には。

なのですぐ転職しようというよりもそのうちに転職しようくらいの温度感だったんですが、組織への不満が蓄積し始めた頃にCyberAgentから声をかけてもらい、以前から働いてみたいと思っていたので早めではありますが転職するに至りました。

前職の組織への不満はネット上に書くのは生々しいので聞きたい方はオフラインで聞いてください。

現職

f:id:lvla:20170427014831j:plain
(俺より強い奴っていうと失礼ですね、すみません。)

なぜ働いてみたかったというとこんな感じでしょうか。

  • 凄腕Androidエンジニアが多数在籍している
  • 動画を扱うプロダクトの開発をしてみたかった
  • AbemaTV, AWA, FRESH!などを見ていまの自分では作れなさそうなUIのクオリティに魅せられた
  • KotlinといえばFRESH!

そして今はFRESH!Androidアプリ開発を行っています。ちょうど今日リニューアル版がリリースされました。

今までModel層をメインに書いていたので、凝ったUIのプロダクトでViewの技術を学ぶ機会を得ることができました。また、動画を扱うプロダクトも初めてのことなのでそこら辺の技術に触れられることも楽しいです。

優秀なエンジニアが多く学ぶことが多いですし、価値に繋がるのであれば新しい技術でもガンガン導入していくスタイルなので刺激的です。

労働環境も良いです。一人あたりのワークスペース広いし、まだ行ってないけど集中部屋とかあるし、リモートワークできるので心に余裕ができました。

それではまた、どこかの勉強会で。

Mock the un-mockable Kotlin classes

f:id:lvla:20170430014523p:plain

tl;dr

  • Kotlinクラスはデフォルトfinalだからモックできない
  • Mockito2ならできるよ

Kotlinのクラス、関数はデフォルトでfinalだからモックできない。

Kotlinのクラス、関数はデフォルトfinalで継承もオーバーライドもできないのでMockitoでモックすることができません。
なので今までは以下のどれかでモック可能な状態にしていました。

  • openで継承可能、オーバーライド可能にする
  • All-open pluginでクラス/関数をすべてopenにする
  • Interfaceに切り出しておく
  • PoweMockを利用する

Mockito2.1.0でfinalでもモックできるようになった

Mockito2.1.0でMockMakerというものが導入されました。ただしMockMakerはまだ実験的段階で、デフォルトではオフになっています。
MockMakerを利用する方法は2つ用意されています。

MockMaker fileを用意する。

src/test/resources/mockito-extensionsディレクトリに以下の一行を含むorg.mockito.plugins.MockMakerファイルを置きます。

mock-maker-inline

たった一行のファイルを置くだけなのでそんなに手間ではないかな?

mockito-inlineを利用する

こちらはgradleでdependenciesを書き換えるだけです。

dependencies {
    //  testCompile "org.mockito:mockito-core:2.x.y"
    testCompile "org.mockito:mockito-inline:2.x.y"
}

どちらを利用するか

私は基本的にMockitoは直接扱っておらず、mockito-coreに依存しているKotlin向けのExtensionライブラリを利用しているのでMockMaker fileを置く方法を利用してます。

DroidKaigi 2017でWelcomeトークと2セッション行ってきた。

Welcomeトーク

なんかいつの間にかWelcomeトークでスピーカー代表として話してました!びっくり! 「当たり前と思ってる情報でも誰かにとっては需要がありますよ!プロポーザル出しましょう!」というお話をしました。 いつのまにかCodeZine, giyho.jpに載ってました。

セッション

speakerdeck.com

speakerdeck.com

Pairsで去年やったおっきな仕事の話と、僕がアプリつくるときによくやる構成の話です。
後者30分に対して詰め込みすぎたーと反省してます。
KotlinはJavaわかればかけるし(と、ヤギさんが言ってました!!!!)、RetrofitやOrmaはリポジトリのREADMEを読めばすすっと使えると思います。
問題はRxJavaとDagger2ですよね。僕も初めてのときは意味がわからなかったんですが何時間かコード書いて覚えました。
とりあえずRxJavaとDagger2の補足資料として以下を置いておきますが、どれかしらを読みながらコードを書いてみてその挙動を確かめるのが一番近道だと思います。

詳解RxJava:Scheduler、非同期処理、subscribe/unsubscribe - Qiita

ユースケースで入門するAndroid RxJava1 - Qiita

逆引きよく使うRxJava1.x オペレータ - Qiita

Dagger2 入門解説 - Qiita

サンプルアプリはこちら github.com

来年が楽しみだね

皆さんお疲れ様でした。DroidKaigi 2018が楽しみですね。