Upgrade to Pro — share decks privately, control downloads, hide ads and more …

個人開発AndroidアプリをKotlinにガチ移行してみた話

 個人開発AndroidアプリをKotlinにガチ移行してみた話

きりみん

June 05, 2015
Tweet

More Decks by きりみん

Other Decks in Technology

Transcript

  1. 個人開発Androidアプリを
    Kotlinにガチ移行してみた話
    2015/6/5
    第3回 かわいいKotlin勉強会
    @kirimin

    View Slide

  2. 自己紹介
    @kirimin
    ● Androidアプリ開発者 (ジャバ)
    ● エンジニア4年目 フリーランス2年目
    ● 現在は圧倒的当事者意識で某社の
    Androidアプリ開発を手伝っています

    View Slide

  3. 自己紹介

    View Slide

  4. View Slide

  5. 今、Androidで
    Kotlinがアツい!!!!

    View Slide

  6. なぜAndroidでKotlin?

    View Slide

  7. なぜAndroidでKotlin?(おさらい)
    ● AndroidではJava8が当分使えなそう
    ● Kotlinならラムダ式もパイプラインでのリスト操作も書ける
    ● RxAndroidとも相性が良い
    ● Android StudioならIDEのサポートが効きすぐに使える
    ● 学習コストも低く、静的で安全志向な言語仕様は   
    Androidエンジニアに丁度いい(主観)
    ● Scalaはいろんな意味で人類にはまだ早い

    View Slide

  8. KotlinでAndroid開発がしたい!!!

    View Slide

  9. でも、本当に使えるのだろうか?

    View Slide

  10. ならば既存アプリを全力で
    Kotlin化してみよう!!!

    View Slide

  11. 今回Kotlin化したアプリ
    https://play.google.com/store/apps/details?id=me.kirimin.mitsumine

    View Slide

  12. mitsumine

    View Slide

  13. mitsumine
    ● 約3500行
    ● 54クラス
    ● 使用しているライブラリ
    ● RxAndroid
    ● Volley
    ● ActiveAndroid
    ● Picasso      など...

    View Slide

  14. やったこと
    ● Kotlin利用準備
    ● JavaクラスをひたすらKotlin化
    ● Kotlin Android Extensionsを導入
    ● もっとKotlinらしくしてみる

    View Slide

  15. やったこと
    ● Kotlin利用準備
    ● JavaクラスをひたすらKotlin化
    ● Kotlin Android Extensionsを導入
    ● もっとKotlinらしくしてみる

    View Slide

  16. Kotlin利用準備
    ● Android StudioにKotlinプラグインをインストール
    ● build.gradleにおまじないを記述
    だいたいこれだけ。
    しかも.ktファイルを作るとポップアップが出てきて自動で
    やってくれる。

    View Slide

  17. やったこと
    ● Kotlin利用準備
    ● JavaクラスをひたすらKotlin化
    ● Kotlin Android Extensionsを導入
    ● もっとKotlinらしくしてみる

    View Slide

  18. JavaクラスをひたすらKotlin化
    「Convert Java to Kotlin File」でひたすらポチポチ…

    View Slide

  19. JavaクラスをひたすらKotlin化
    「Convert Java to Kotlin File」でひたすらポチポチ…
    ● クラス数が多いと結構だるい… (一斉変換も出来るけど)
    ● 大抵は自動変換しただけでちゃんと動く
    ● 時々コンパイルエラーが出る
    ● 時々実行時エラーが出る
    ● 時々変な変換されて処理が消える
    ● 1クラス変換する度にDiffって動作確認した方がハマらない(戒め)

    View Slide

  20. JavaクラスをひたすらKotlin化
    変換時に遭遇したエラーの思い出
    ● Null許容型とNull非許容型で型エラーが出てる
    →型に?を付けてNull許容型に変えたりして頑張ろう
    ● overrideメソッドのsuperでエラーが出てる
    →superのようにどのスーパークラスを呼ぶのか
    明示しよう
    ● CustomViewのコンストラクタでエラーが出てる
     →class xxxView: View()の()を消してsecondary constructor
    の構文を使おう

    View Slide

  21. ● Kotlin利用準備
    ● JavaクラスをひたすらKotlin化
    ● Kotlin Android Extensionsを導入
    ● もっとKotlinらしくしてみる
    やったこと

    View Slide

  22. Kotlin Android Extensionsを導入
    Kotlin Android Extensionsとは
    ● KotlinのAndroid用公式ライブラリ(プラグイン)
    ● findViewByIdを葬り去るためのライブラリ
    ● Activityクラスのimport文でActivityとレイアウトファイルを紐
    付けるだけでActivityにViewのプロパティを生やしてくれる
    ● Butter Knifeの@InjectViewすら必要ない版のようなイメージ
    ● 手軽に使えてめちゃくちゃ便利

    View Slide

  23. Kotlin Android Extensionsを導入
     import kotlinx.android.synthetic..*
    ActivityやFramentにレイアウトファイルを紐付ける。
     userNameTextView.setText(“test”);
    Activityのプロパティになるのでそのまま参照出来る。
     getView().userNameTextView.setText(“test”);
    Fragmentの場合はgetView()で取得出来るルートのViewに生える。

    View Slide

  24. ● Kotlin利用準備
    ● JavaクラスをひたすらKotlin化
    ● Kotlin Android Extensionsを導入
    ● もっとKotlinらしくしてみる
    やったこと

    View Slide

  25. もっとKotlinらしくしてみる
    匿名クラスをラムダ式に書き換える
    textView.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View) {
    }
    })
    textView.setOnClickListener { v ->
    }

    View Slide

  26. もっとKotlinらしくしてみる
    匿名クラスのままKotlinに変換されたRxJavaの処理も
    subscriptions.add(EntryInfoApi.request(RequestQueueSingleton.get(getApplicationContext()), url)
    .subscribeOn(Schedulers.newThread())
    .observeOn(AndroidSchedulers.mainThread())
    .map(EntryInfoFunc.mapToEntryInfo())
    .filter(object : Func1 {
    override fun call(entryInfo: EntryInfo?): Boolean? {
    return entryInfo == null
    }
    })
    .subscribe(object : Action1 {
    override fun call(entryInfo: EntryInfo) {
    countLayout.setVisibility(View.VISIBLE)
    titleTextView.setText(entryInfo.getTitle())
    bookmarkCountTextView.setText(String.valueOf(entryInfo.getBookmarkCount()))
    Picasso.with(getApplicationContext()).load(entryInfo.getThumbnailUrl()).fit().into(thumbnailImageView)
    val adapter = EntryInfoPagerAdapter(getSupportFragmentManager())
    adapter.addPage(BookmarkListFragment.newFragment(entryInfo.getBookmarkList()), getString(R.string.entry_info_all_bookmarks))
    subscriptions.add(Observable.from(entryInfo.getBookmarkList())
    .filter(EntryInfoFunc.hasComment())
    .toList()
    .subscribe(object : Action1> {
    override fun call(commentList: List) {
    commentCountTextView.setText(String.valueOf(commentList.size()))
    adapter.addPage(BookmarkListFragment.newFragment(commentList), getString(R.string.entry_info_comments))
    if (AccountDAO.get() != null) {
    adapter.addPage(RegisterBookmarkFragment.newFragment(entryInfo.getUrl()), getString(R.string.entry_info_register_bookmark))
    }
    viewPager.setAdapter(adapter)
    viewPager.setCurrentItem(1)
    viewPager.setOffscreenPageLimit(2)
    tabs.setViewPager(viewPager)
    }
    }))
    }
    }, object : Action1 {
    override fun call(throwable: Throwable) {
    Toast.makeText(getApplicationContext(), R.string.network_error, Toast.LENGTH_SHORT).show()
    }

    View Slide

  27. もっとKotlinらしくしてみる
    全てラムダ式にしてスッキリ!
    subscriptions.add(EntryInfoApi.request(RequestQueueSingleton.get(getApplicationContext()), url)
    .subscribeOn(Schedulers.newThread())
    .observeOn(AndroidSchedulers.mainThread())
    .map { response -> EntryInfoFunc.toEntryInfo(response) }
    .filter { entryInfo -> !entryInfo.isNullObject() }
    .subscribe ({ entryInfo ->
    countLayout.setVisibility(View.VISIBLE)
    titleTextView.setText(entryInfo.title)
    bookmarkCountTextView.setText(entryInfo.bookmarkCount.toString())
    Picasso.with(getApplicationContext()).load(entryInfo.thumbnailUrl).fit().into(thumbnailImageView)
    val adapter = EntryInfoPagerAdapter(getSupportFragmentManager())
    adapter.addPage(BookmarkListFragment.newFragment(entryInfo.bookmarkList), getString(R.string.entry_info_all_bookmarks))
    subscriptions.add(Observable.from(entryInfo.bookmarkList)
    .filter { bookmark -> EntryInfoFunc.hasComment(bookmark) }
    .toList()
    .subscribe { commentList ->
    commentCountTextView.setText(commentList.size().toString())
    adapter.addPage(BookmarkListFragment.newFragment(commentList), getString(R.string.entry_info_comments))
    AccountDAO.get()?.let {
    adapter.addPage(RegisterBookmarkFragment.newFragment(entryInfo.url), getString(R.string.entry_info_register_bookmark))
    }
    commentsViewPager.setAdapter(adapter)
    commentsViewPager.setCurrentItem(1)
    commentsViewPager.setOffscreenPageLimit(2)
    tabs.setViewPager(commentsViewPager)
    })
    }, { Toast.makeText(getApplicationContext(), R.string.network_error, Toast.LENGTH_SHORT).show() })

    View Slide

  28. もっとKotlinらしくしてみる
    リスト操作を高階関数に書き換える
    for (i in list) {
    if (i % 2 == 0) {
    System.out.print(i)
    }
    }
    list.filter { i -> i % 2 == 0 }
    .forEach { i -> System.out.print(i) }

    View Slide

  29. もっとKotlinらしくしてみる
    Callback用の自前Interface(trait)も
    public trait TestCallback {
    public fun onSuccess(callback: Int)
    }
    public fun request(callback: TestCallback) {
    callback.onSuccess(200)
    }
    // 呼び出し元
    test.request(object : Test.TestCallback {
    override fun onSuccess(callback: Int) {
    }
    })

    View Slide

  30. もっとKotlinらしくしてみる
    高階関数で直接処理を受け取ればシンプルに出来ます
    public fun request(onSuccess: (callback: Int) -> Unit) {
    onSuccess(200)
    }
    // 呼び出し元
    test.request { callback ->
    }

    View Slide

  31. まとめ

    View Slide

  32. まとめ
    ● Kotlin置換+αの対応だけで3500行→3000行くらいにコード
    をスリム化!
    ● Javaで設計されたコードの移行はわりと大変だった    
    (単純置換だけだとかえって読みにくいコードになるかも)
    ● とはいえAndroidアプリの開発をガッツリKotlinでやる事自体
    の不便さや辛さはほとんど感じなかった          
    (ライブラリなどもそのまま使えたよ)
    ● むしろ一度Kotlinを味わうとAndroidJavaでリスナーや  
    リスト操作を書くのが辛くなる

    View Slide

  33. まとめ
    ● 個人開発の新規アプリや期間限定の出し切りアプリなどであれ
    ばKotlinは現実的な選択肢では
    ● 個人開発の既存アプリを書き換えるべきかはモチベーション 
    次第だけどメリットは十分あると思う
    ● 業務で導入する場合、標準じゃない技術に依存するリスクを 
    メリットが上回るかは………

    View Slide

  34. まとめ
    ● 詳しい知見はコードをご確認ください
    https://github.com/kirimin/mitsumine

    View Slide

  35. 誰か業務で使った実績
    でっち上げてくれ!!!!

    View Slide

  36. ご清聴ありがとうございました!

    View Slide