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

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

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

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

Avatar for きりみん

きりみん

June 05, 2015
Tweet

More Decks by きりみん

Other Decks in Technology

Transcript

  1. なぜAndroidでKotlin?(おさらい) • AndroidではJava8が当分使えなそう • Kotlinならラムダ式もパイプラインでのリスト操作も書ける • RxAndroidとも相性が良い • Android StudioならIDEのサポートが効きすぐに使える

    • 学習コストも低く、静的で安全志向な言語仕様は    Androidエンジニアに丁度いい(主観) • Scalaはいろんな意味で人類にはまだ早い
  2. mitsumine • 約3500行 • 54クラス • 使用しているライブラリ • RxAndroid •

    Volley • ActiveAndroid • Picasso      など...
  3. JavaクラスをひたすらKotlin化 「Convert Java to Kotlin File」でひたすらポチポチ… • クラス数が多いと結構だるい… (一斉変換も出来るけど) • 大抵は自動変換しただけでちゃんと動く

    • 時々コンパイルエラーが出る • 時々実行時エラーが出る • 時々変な変換されて処理が消える • 1クラス変換する度にDiffって動作確認した方がハマらない(戒め)
  4. Kotlin Android Extensionsを導入 Kotlin Android Extensionsとは • KotlinのAndroid用公式ライブラリ(プラグイン) • findViewByIdを葬り去るためのライブラリ

    • Activityクラスのimport文でActivityとレイアウトファイルを紐 付けるだけでActivityにViewのプロパティを生やしてくれる • Butter Knifeの@InjectViewすら必要ない版のようなイメージ • 手軽に使えてめちゃくちゃ便利
  5. もっとKotlinらしくしてみる 匿名クラスのままKotlinに変換されたRxJavaの処理も subscriptions.add(EntryInfoApi.request(RequestQueueSingleton.get(getApplicationContext()), url) .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .map<EntryInfo>(EntryInfoFunc.mapToEntryInfo()) .filter(object : Func1<EntryInfo,

    Boolean> { override fun call(entryInfo: EntryInfo?): Boolean? { return entryInfo == null } }) .subscribe(object : Action1<EntryInfo> { 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<Bookmark>(entryInfo.getBookmarkList()) .filter(EntryInfoFunc.hasComment()) .toList() .subscribe(object : Action1<List<Bookmark>> { override fun call(commentList: List<Bookmark>) { 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<Throwable> { override fun call(throwable: Throwable) { Toast.makeText(getApplicationContext(), R.string.network_error, Toast.LENGTH_SHORT).show() }
  6. もっと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<Bookmark>(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() })
  7. もっと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) } ↓
  8. もっと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) { } })