Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Kotlinを始めようハンズオン #DroidKaigi #DroidKaigi6
Search
Taro Nagasawa
March 10, 2017
Programming
5
6.3k
Kotlinを始めようハンズオン #DroidKaigi #DroidKaigi6
DroidKaigi2017 (
https://droidkaigi.github.io/2017/
) で発表したスライドです。
Taro Nagasawa
March 10, 2017
Tweet
Share
More Decks by Taro Nagasawa
See All by Taro Nagasawa
Android開発者のための Kotlin Multiplatform入門
ntaro
0
340
Kotlin 最新動向2022 #tfcon #techfeed
ntaro
1
2.1k
#Ubie 狂気の認知施策と選考設計
ntaro
13
12k
UbieにおけるサーバサイドKotlin活用事例
ntaro
1
1.1k
KotlinでSpring 完全理解ガイド #jsug
ntaro
6
3.3k
Kotlinでサーバサイドを始めよう!
ntaro
1
920
Androidからサーバーサイドまで!プログラミング言語 Kotlinの魅力 #devboost
ntaro
5
2.5k
Kotlin Contracts #m3kt
ntaro
4
3.8k
How_to_Test_Server-side_Kotlin.pdf
ntaro
1
430
Other Decks in Programming
See All in Programming
NSOutlineView何もわからん:( 前編 / I Don't Understand About NSOutlineView :( Pt. 1
usagimaru
0
160
ECS Service Connectのこれまでのアップデートと今後のRoadmapを見てみる
tkikuc
2
210
AWS IaCの注目アップデート 2024年10月版
konokenj
3
3.1k
Realtime API 入門
riofujimon
0
110
CSC509 Lecture 09
javiergs
PRO
0
110
EventSourcingの理想と現実
wenas
6
2.1k
2万ページのSSG運用における工夫と注意点 / Vue Fes Japan 2024
chinen
3
1.4k
【Kaigi on Rails 2024】YOUTRUST スポンサーLT
krpk1900
1
250
約9000個の自動テストの 時間を50分->10分に短縮 Flakyテストを1%以下に抑えた話
hatsu38
23
11k
破壊せよ!データ破壊駆動で考えるドメインモデリング / data-destroy-driven
minodriven
16
4.1k
デプロイを任されたので、教わった通りにデプロイしたら障害になった件 ~俺のやらかしを越えてゆけ~
techouse
52
32k
GCCのプラグインを作る / I Made a GCC Plugin
shouth
1
150
Featured
See All Featured
Building an army of robots
kneath
302
42k
Code Reviewing Like a Champion
maltzj
519
39k
What's new in Ruby 2.0
geeforr
342
31k
Thoughts on Productivity
jonyablonski
67
4.3k
Large-scale JavaScript Application Architecture
addyosmani
510
110k
Done Done
chrislema
181
16k
Happy Clients
brianwarren
97
6.7k
Put a Button on it: Removing Barriers to Going Fast.
kastner
59
3.5k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
25
1.8k
Rails Girls Zürich Keynote
gr2m
93
13k
Music & Morning Musume
bryan
46
6.1k
Building a Scalable Design System with Sketch
lauravandoore
459
33k
Transcript
Kotlinを始めようハンズオン 2017-03-10 DroidKaigi 2017 長澤太郎 @ngsw_taro
!!サンプルプロジェクト!! • クローンしておいてください • prepareブランチをビルドしておいてください ◦ ダウンロードが大量に発生します • Android Studio
2.3対応のものを「-as23」というsuffixで各 ブランチを用意しています github.com/ntaro/github-client-for-droidkaigi
今日やること • AndroidプロジェクトにKotlinを導入する ◦ 実際に手を動かし、Kotlin導入の手軽さを体験する • KotlinをAndroidアプリ開発に活用する ◦ 簡単なサンプルアプリの開発を通じ、Kotlinの便利さを学ぶ •
Kotlinの弱点を知り、フォローする ◦ 特にJavaとの相互運用性を理解し、弱点を回避する
今日やらないこと • Androidおさらい • その他の言語(Javaなど) • アプリの作り込み(あくまでサンプルアプリ) • プログラミングの考え方や手法(オブジェクト指向など) •
ライブラリ、フレームワーク • データ構造、アルゴリズム • セキュリティ
もくじ 1. Kotlin概要 5分 2. 開発環境の準備 3分 3. AndroidでKotlinを始める 12分(7分)
4. Githabクライアントを作る 13分(8分) 5. リポジトリ詳細画面への遷移 20分(13分) 6. Retrofitを使ってAPIを叩く 15分(12分) 7. DaggerでDIする 10分(5分) 8. Kotlin 1.1の機能を使う 10分(5分) 9. おわりに 2分 ※カッコ内は課題に取り組む時間
• 長澤 太郎 たろーって呼んでね • @ngsw_taro 自己紹介
エムスリー株式会社 jobs.m3.com/engineer Wantedly記事 goo.gl/BxK5ZZ
エバンジェリストな私 • Kotlin歴 5年 • 日本Kotlinユーザグループ代表 • 講演実績多数 ◦ DroidKaigi
2015, 2016 ◦ JJUG CCC 2015 Fall ◦ 福岡、京都など遠征も • 執筆実績多数 ◦ 単行本、商業誌、同人誌
チューター紹介 • yy_yank • JavaとKotlin好きなプロ グラマ。仕事はゲーム開発 左右前後で困っている人がいたら助けてあげましょう! • @RyotaMurohoshi •
お仕事はKotlinで Android。趣味はC#で Unity。
1. Kotlin概要
Kotlinとは? • Java仮想マシンをターゲットとしたプログラミング言語 ◦ JavaScriptやAndroidもサポート • IntelliJ IDEAでおなじみのJetBrainsが開発 • 2011年に発表され、2016年2月にver1.0がリリース
• 現在 ver1.1.0 • 静的型付けオブジェクト指向言語 • 簡潔、安全、Javaとの相互運用性
Hello World package sample fun main(args: Array<String>) { if(args.isEmpty()) return
val name = args[0] println("Hello, ${name}!") }
Kotlinの特徴 • 簡潔 ◦ モダンな文法、型推論、ラムダ式 ◦ 拡張関数、委譲プロパティ、コルーチン • 安全 ◦
型安全 ◦ NULL安全 • Javaとの相互運用性 ◦ KotlinからJavaコードを呼び出せる。その逆も然り ◦ Java用ツールやFWからKotlinが呼び出されるときハマりやすい
2. 開発環境の準備 目標 16:05
必要なもの • JDK, Android SDK • Android Studio • Kotlin
Plugin
サンプルプロジェクト • クローンしておいてください • ハンズオンではhandsonブランチを使用するので、これを チェックアウトしておいてください • Android Studio 2.3対応のものを「-as23」というsuffixで各
ブランチを用意しています github.com/ntaro/github-client-for-droidkaigi
3. AndroidでKotlinを始める 目標 16:08
Kotlinコードを最速でAndroidで動かす方法 1. File -> New Project… 2. すべてデフォルトの設定でOK 3. ウィザードのFinishボタンを押す
4. Tools -> Kotlin -> Configure Kotlin in Project 5. 「Kotlin 1.1」になっていることを確認してOK 6. Code -> Convert Java File to Kotlin File 7. javaディレクトリを「kotlin」にリネーム 8. ビルド&実行 やってみよう(7分)
おめでとう! これであなたも Kotlinプログラマ!
簡単に解説 class MainActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } }
簡単に解説 class MainActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } 継承 + スーパクラスのコンストラクタ呼び出し
簡単に解説 class MainActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } オーバライドするために必須
簡単に解説 class MainActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } } BundleのNullable型(null許容型)
4. Githubクライアントを作る 目標 16:20
こういう簡単なやつ
サンプルプロジェクト • クローンしておいてください • ハンズオンではhandsonブランチを使用するので、これを チェックアウトしておいてください • Android Studio 2.3対応のものを「-as23」というsuffixで各
ブランチを用意しています github.com/ntaro/github-client-for-droidkaigi
Kotlinでクラス定義 class Person(val id: Long?, val name: String) val taro
= Person(123, "Taro") taro.id //=> 123 taro.name //=> Taro
Kotlinでクラス定義 class Person(val id: Long?, val name: String) val taro
= Person(123, "Taro") taro.id //=> 123 taro.name //=> Taro これがクラス
Kotlinでクラス定義 class Person(val id: Long?, val name: String) val taro
= Person(123, "Taro") taro.id //=> 123 taro.name //=> Taro プライマリコンストラクタ
Kotlinでクラス定義 class Person(val id: Long?, val name: String) val taro
= Person(123, "Taro") taro.id //=> 123 taro.name //=> Taro プロパティ キーワードvalがミソ
Kotlinでクラス定義 class Person(val id: Long?, val name: String) val taro
= Person(123, "Taro") taro.id //=> 123 taro.name //=> Taro コンストラクタを呼び出して インスタンスを変数に代入
Kotlinでクラス定義 class Person(val id: Long?, val name: String) val taro
= Person(123, "Taro") taro.id //=> 123 taro.name //=> Taro プロパティにアクセス
課題1. リポジトリクラスを作ろう(4分) • Github上に存在するリポジトリを表現するクラスを定義しま しょう。 • sample.githubclient.model.Repository プロパティ名 型 説明
id Long ID fullName String フルネーム=「ユーザ名 /リポジトリ名」 description String リポジトリの説明 htmlUrl String 詳細URL stargazersCount Int スター数 owner User リポジトリの所有者。 Userクラスは定義済み language String? 言語
解答例 class Repository(val id: Long, val fullName: String, val description:
String, val htmlUrl: String, val stargazersCount: Int, val owner: User, val language: String?)
課題2. ダミーデータをリスト表示しよう(4分) 1. 作成したRepositoryクラスのインスタンスをいくつか生成す る 2. 標準関数listOfを使用して、リストを生成する e.g.) val ints
= listOf(1, 2, 3) 3. listAdapterのrepositoriesプロパティに、表示対象の リポジトリリストをセットする 4. RepositoryViewクラスのコメントアウトを外す
解答例 val user = User(1, "ntaro", "https://example.com") val repository =
Repository(2, "ntaro/Sample", "sample Project", "https://example.com", 3, user, "Kotlin") listAdapter = listOf(repository)
5. リポジトリ詳細画面への遷移 目標 16:33
リスト項目をタップして、詳細画面へ
リスト項目をタップして、詳細画面へ Repotiroyオブ ジェクトをインテ ントに載せて渡 す
Parcelable • Parcelにデータを書くために実装すべき形式・ルール • 保存: Parcelableインタフェースを実装する • 復元: Parcelable.Creatorインタフェースを実装したオブ ジェクトをstaticフィールドCREATORとして公開する
Parcelable • Parcelにデータを書くために実装すべき形式・ルール • 保存: Parcelableインタフェースを実装する • 復元: Parcelable.Creatorインタフェースを実装したオブ ジェクトをstaticフィールドCREATORとして公開する
Kotlinには staticもフィールドもない!
コンパニオンオブジェクト class Foo { companion object { val name: String
= "Foo" } } Foo.name //=> Foo
コンパニオンオブジェクト class Foo { companion object { val name: String
= "Foo" } } Foo.name //=> Foo コンパニオンオブジェクト
コンパニオンオブジェクト class Foo { companion object { val name: String
= "Foo" } } Foo.name //=> Foo staticっぽい! フィールドっぽい! →でも違う
JVMと仲良くするアノテーション class Foo { companion object { @JvmField val name:
String = "Foo" } }
JVMと仲良くするアノテーション class Foo { companion object { @JvmField val name:
String = "Foo" } } staticフィールドとして見えるようになる
課題3. Parcelableを実装しよう(6分) • sample.githubclient.model.Repositoryクラス • Userクラスが参考になる ヒント: オブジェクト式(匿名クラス) val onClickListener
= object: View.OnClickListener { override fun onClick(view: View) { ... } }
よくあるインテント生成するやつ // Java class RepositoryActivity extends AppCompatActivity { static Intent
intent(Context context, Repository repository) { return new Intent(context, RepositoryActivity.class) .putExtra("repository", repository) } ... }
使う側 Intent intent = RepositoryActivity.intent(this, repository); startActivity(intent);
課題4. 起動用インテント生成関数を提供する(4分) • sample.githubclient.RepositoryActivityクラス ヒント: 単一式関数 fun intent(context: Context): Intent
{ return Intent(...) } fun intent(context: Context: Intent = Intent(...) 同じ ヒント: Class<Foo>オブジェクトの取得 ずばりFoo::class.java • Foo:class ←Kotlin用リフレクション KClass<Foo> • Foo:class.java ←KClassの拡張プロパティ
便利な拡張関数を定義する intent(context, MyActivity::class.java) fun <T:Any> Context.intent(kClass: KClass<T>) = Intent(this, kClass.java)
context.intent(MyActivity::class) inline fun <reified T:Any> Context.intent() = Intent(this, T::class.java) context.intent<MyActivity>()
便利な拡張関数を定義する intent(context, MyActivity::class.java) fun <T:Any> Context.intent(kClass: KClass<T>) = Intent(this, kClass.java)
context.intent(MyActivity::class) inline fun <reified T:Any> Context.intent() = Intent(this, T::class.java) context.intent<MyActivity>() Contextにメソッドを生やすイメージ
便利な拡張関数を定義する intent(context, MyActivity::class.java) fun <T:Any> Context.intent(kClass: KClass<T>) = Intent(this, kClass.java)
context.intent(MyActivity::class) inline fun <reified T:Any> Context.intent() = Intent(this, T::class.java) context.intent<MyActivity>() KClassを引数に、Classをここで取る ←記述がスッキリ
便利な拡張関数を定義する intent(context, MyActivity::class.java) fun <T:Any> Context.intent(kClass: KClass<T>) = Intent(this, kClass.java)
context.intent(MyActivity::class) inline fun <reified T:Any> Context.intent() = Intent(this, T::class.java) context.intent<MyActivity>() 関数のインライン化により、消えるはずの型引数がランタイムで使える
便利な拡張関数を定義する intent(context, MyActivity::class.java) fun <T:Any> Context.intent(kClass: KClass<T>) = Intent(this, kClass.java)
context.intent(MyActivity::class) inline fun <reified T:Any> Context.intent() = Intent(this, T::class.java) context.intent<MyActivity>() ←非常に目に優しい
課題5. 詳細画面を起動しよう(4分) • sample.githubclient.MainActivityクラス • listView.setOnItemClickListenerメソッドでリスナを登録 する • listAdapter.repositories[position]で、指定位置のリ ポジトリオブジェクトを取得できる
ヒント: SAM変換 Javaで定義された「ただひとつの抽象メソッドを持った型を引数に取るメソッド」 に対して、ラムダ式を渡すことができる機能。 listView.setOnItemClickListener { _, _, position, _ -> /* クリックされたときの処理 */ }
課題6. リポジトリの情報を表示しよう(4分) • sample.githubclient.RepositoryActivityクラス • インテントから受け取ったリポジトリを表示する • findViewByIdでビューを取得(IDはコメントとして記載済) • repositoryView.setRepository(repository)
• webView.loadUrl(repository.htmlUrl) ヒント: キャスト val textView = findViewById(R.id.text_view) as TextView
6. Retrofitを使ってAPIを叩く 目標 16:53
課題7. 検索APIを叩くメソッドを提供しよう(4分) • sample.githubclient.GithubClientインタフェース • @GET("/search/repositories") • @Query("q") • 戻り型
Call<Page<Repository>> • パッケージに注意 ◦ retrofit2.Call ◦ sample.githubclient.model.Page ヒント: インタフェース Javaのインタフェースと基本的に同じです。 抽象メソッドを宣言するのに、abstractキーワードは省略可能であり、普通 記述しません。
課題8. GithubClientで検索を実行しよう(8分) • sample.githubclient.MainActivityクラス • 既に設定済みのRetrofitオブジェクトが用意されている • retrofit.create(GithubClient::class.java)で GithubClientの実装を取得する •
searchButtonのクリック時に検索を非同期で開始 ◦ Call#enqueueメソッドを呼び出す ◦ 引数にコールバックを指定 • 検索結果(Page<Repository>)からRepositoryリストを 取り出して、リストに反映する ◦ listAdapter.repositoriesプロパティ ◦ listAdapter.notifyDataSetChanged()メソッド
7. DaggerでDIする 目標 17:08
kapt • Daggerのようなコンパイル時にアノテーションを見て、面白い ことをしてくれるツールを、Kotlinでも使えるように apply plugin: 'kotlin-kapt' dependencies { compile
"com.google.dagger:dagger:$version" kapt "com.google.dagger:dagger-compiler:$version" provided 'javax.annotation:jsr250-api:1.0' }
フィールドインジェクション // Java @Inject GithubClient githubClient; // 暗黙的に初期値はnull // Kotlin
@JvmField @field:Inject var githubClient: GithubClient? = null
フィールドインジェクション // Java @Inject GithubClient githubClient; // 暗黙的に初期値はnull // Kotlin
@JvmField @field:Inject var githubClient: GithubClient? = null 無理やりフィールド化
フィールドインジェクション // Java @Inject GithubClient githubClient; // 暗黙的に初期値はnull // Kotlin
@JvmField @field:Inject var githubClient: GithubClient? = null フィールドに対してアノテート
フィールドインジェクション // Java @Inject GithubClient githubClient; // 暗黙的に初期値はnull // Kotlin
@JvmField @field:Inject var githubClient: GithubClient? = null 初期値を明示
lateinit修飾子 @Inject lateinit var githubClient: GithubClient
課題9. GithubClientの実装をInjectしよう(5分) • sample.githubclient.MainActivityクラス • githubClientプロパティを置いて、そこに実装をインジェクトさせ る ◦ @Inject ◦
lateinit
8. Kotlin 1.1の機能を使う 目標 17:18
Kotlin 1.1が3/1にリリースされた! • コルーチン • Javaのようなメソッド参照 ◦ foo.map(bar::method) • ラムダ式の引数での分解
◦ Pair("one", 1).let { (name, value) -> ... } • 型エイリアス ◦ typealias OnClickListener = (View) -> Unit • ローカル委譲プロパティ ◦ fun foo() { val s: String by lazy { "hello" } println(s) }
コルーチンを利用したライブラリを使う • 3rdパーティ製 async / await for Android • metalabdesign/AsyncAwait
kotlin { experimental { coroutines 'enable' } } dependencies { compile 'co.metalab.asyncawait:asyncawait:1.0.0' }
使い方 button.setOnClickListener { async { val data = await {
時間のかかる処理() } textView.text = data } } 処理の流れ コールバック地獄から解放される
Retrofitでは async { val data1 = await { getData().execute().body() }
val data2 = awaitSuccessful(getData()) textView.text = "$data1/$data2" }
Retrofitでは async { val data1 = await { getData().execute().body() }
val data2 = awaitSuccessful(getData()) textView.text = "$data1/$data2" } 別スレッドでの処理&待ち合わせ
Retrofitでは async { val data1 = await { getData().execute().body() }
val data2 = awaitSuccessful(getData()) textView.text = "$data1/$data2" } RetrofitのCall用メソッド
課題10. async/awaitを使ってみよう(5分) • sample.githubclient.MainActivityクラス • asyncブロックを展開する • awaitブロック内は別スレッドで実行され、結果を待つ • Retrofit用のawaitSuccessfulが便利
• asyncブロックでビューの更新が可能 オプション: エラーハンドリング async { ... }.onError { e: Exception -> ... }
9. おわりに 目標 17:28
今日学んだこと • AndroidでKotlinを導入するのは楽チン♪ • Kotlinの文法がなんとなくわかった • まぁ普通にJavaライブラリが使えるよ! ◦ Retrofit ◦
Dagger2 • Javaとの違い、共存の仕方 ◦ RepositoryActivity::class.java ◦ @JvmFieldによるフィールド化 ◦ Kotlinの弱点→Javaからの見え方を意識すべし • コード量が少なくなり、目に優しい! ◦ 拡張関数 ◦ コルーチン
ご静聴ありがとうございました 質問や不明点があったら... → Q&Aは時間取れないので → このあとの懇親会で! → Twitterでも何でも気軽に声かけてください! → 日本語
Kotlin Slackも! http://kotlinlang-jp.herokuapp.com → teratailでQ&Aを共有 (Kotlinエキスパートユーザ) @ngsw_taro 再演・講演依頼、執 筆依頼などお待ちし てます!