scala for android explained

219
Scala äëÿ Android. Ìèô èëè ðåàëüíîñòü Матвей Мальков

Upload: matvey-malkov

Post on 11-Apr-2017

263 views

Category:

Technology


0 download

TRANSCRIPT

Scala äëÿ Android. Ìèô èëè ðåàëüíîñòüМатвей Мальков

2Обо мне

3Обо мне

4Обо мне

5Обо мне

6

7Titanium messenger

• end-to-end шифрование

7Titanium messenger

• end-to-end шифрование

• качественный продукт, не MVP

7Titanium messenger

• end-to-end шифрование

• качественный продукт, не MVP

• повышенная безопасность

7Titanium messenger

• end-to-end шифрование

• качественный продукт, не MVP

• повышенная безопасность

• p2p чаты и звонки

7Titanium messenger

• end-to-end шифрование

• качественный продукт, не MVP

• повышенная безопасность

• p2p чаты и звонки

• в релизе

7Titanium messenger

Ïîëîæåíèå äåë

9Ïîëîæåíèå äåë

• java 7 версии

9Ïîëîæåíèå äåë

• java 7 версии

• лямбды есть

9Ïîëîæåíèå äåë

• java 7 версии

• лямбды есть

• ничего больше нет

9Ïîëîæåíèå äåë

• java 7 версии

• лямбды есть

• ничего больше нет

• новые языки

9Ïîëîæåíèå äåë

• java 7 версии

• лямбды есть

• ничего больше нет

• новые языки

• лаконичные

9Ïîëîæåíèå äåë

• java 7 версии

• лямбды есть

• ничего больше нет

• новые языки

• лаконичные

• с хорошим API

9Ïîëîæåíèå äåë

• java 7 версии

• лямбды есть

• ничего больше нет

• новые языки

• лаконичные

• с хорошим API

• JVM-based

9Ïîëîæåíèå äåë

10Ïðîáëåìû ðàçðàáîòêè

• много корнеркейсов

10Ïðîáëåìû ðàçðàáîòêè

• много корнеркейсов

• уродливость кода

10Ïðîáëåìû ðàçðàáîòêè

• много корнеркейсов

• уродливость кода

• сложные конструкции

10Ïðîáëåìû ðàçðàáîòêè

• много корнеркейсов

• уродливость кода

• сложные конструкции

• ***Fragment на 3500 строк кода

10Ïðîáëåìû ðàçðàáîòêè

• много корнеркейсов

• уродливость кода

• сложные конструкции

• ***Fragment на 3500 строк кода

• многопоточность

10Ïðîáëåìû ðàçðàáîòêè

11JVM-ñåìåéñòâî

• Kotlin

11JVM-ñåìåéñòâî

• Kotlin

• Scala

11JVM-ñåìåéñòâî

• Kotlin

• Scala

• Clojure

11JVM-ñåìåéñòâî

• Kotlin

• Scala

• Clojure

• Groovy

11JVM-ñåìåéñòâî

Scala

13Scala

• ООП + ФП

13Scala

• ООП + ФП

• супер синтаксис

13Scala

• ООП + ФП

• супер синтаксис

• REPL

13Scala

• ООП + ФП

• супер синтаксис

• REPL

• полностью java-совместима

13Scala

• ООП + ФП

• супер синтаксис

• REPL

• полностью java-совместима

• почти

13Scala

• ООП + ФП

• супер синтаксис

• REPL

• полностью java-совместима

• почти

• работа с коллекциями

13Scala

• ООП + ФП

• супер синтаксис

• REPL

• полностью java-совместима

• почти

• работа с коллекциями

• примеси, лямбды, фьючи и т.д.

13Scala

14

case class User(name: String, age: Int)

15

case class User(name: String, age: Int) val u = User("Matvey", 16)

16

case class User(name: String, age: Int) val u = User("Matvey", 16)println(u.name)

17

case class User(name: String, age: Int) val u = User("Matvey", 16)println(u.name) !//User(Matvey,16)

18

def doSmth(u: User): Response = { !//code here}

19

def doSmth(u: User): Response = { !//code here}var u = User("Matvey", 16)

u = User("Boris", 21)

Çà÷åì ðàçðàáàòûâàòü íà Scala ïîä Android?

21Çà÷åì ìíå ýòî?

• безопасность

21Çà÷åì ìíå ýòî?

• безопасность

• разделение и переиспользование

21Çà÷åì ìíå ýòî?

• безопасность

• разделение и переиспользование

• хорошая архитектура

21Çà÷åì ìíå ýòî?

• безопасность

• разделение и переиспользование

• хорошая архитектура

• легкое построение DSL и работа с UI

21Çà÷åì ìíå ýòî?

1. Áåçîïàñíîñòü

23Option

• простой АТД

23Option

• простой АТД

• 2 варианта

23Option

• простой АТД

• 2 варианта

• Some(smth)

23Option

• простой АТД

• 2 варианта

• Some(smth)

• None

23Option

• простой АТД

• 2 варианта

• Some(smth)

• None

• никаких null

23Option

• простой АТД

• 2 варианта

• Some(smth)

• None

• никаких null

• куча методов для работы

23Option

24

case class User(name: String, age: Option[Int])

25

case class User(name: String, age: Option[Int]) val m = User("Matvey", Some(16))val b = User("Boris", None)

26

case class User(name: String, age: Option[Int]) val m = User("Matvey", Some(16))val b = User("Boris", None)m.age.foreach(println)

27

case class User(name: String, age: Option[Int]) val m = User("Matvey", Some(16))val b = User("Boris", None)m.age.foreach(println)val borisAge = b.age.getOrElse(27)

28

case class User(name: String, age: Option[Int]) val m = User("Matvey", Some(16))val b = User("Boris", None)m.age.foreach(println)val borisAge = b.age.getOrElse(27) m.age.filter(_ > 18).map(makeDrink).foreach(_.drink)

29

val s = Option(getArguments.getString(key))

30

implicit class AnyToOption[T](x: T) { def option: Option[T] = Option(x)}

val s = Option(getArguments.getString(key))

31

implicit class AnyToOption[T](x: T) { def option: Option[T] = Option(x)}

val s = getArguments.getString(key).option

val s = Option(getArguments.getString(key))

32

implicit class AnyToOption[T](x: T) { def option: Option[T] = Option(x)}

val s = getArguments.getString(key).option

val s = Option(getArguments.getString(key))

s.foreach { arg #=> initUiWithArgument(arg)}

33val m = User("Matvey", 16.option)val b = User("Boris", None)val y = User("Yana", 19.option)val users = Seq(m, b, y)

34val m = User("Matvey", 16.option)val b = User("Boris", None)val y = User("Yana", 19.option)val users = Seq(m, b, y)

35val m = User("Matvey", 16.option)val b = User("Boris", None)val y = User("Yana", 19.option)val users = Seq(m, b, y)println(users(0).name)

36val m = User("Matvey", 16.option)val b = User("Boris", None)val y = User("Yana", 19.option)val users = Seq(m, b, y)println(users(0).name) !//плохо

37val m = User("Matvey", 16.option)val b = User("Boris", None)val y = User("Yana", 19.option)val users = Seq(m, b, y)println(users(0).name) !//плохоusers.headOption.foreach(println) !//хорошо

38val m = User("Matvey", 16.option)val b = User("Boris", None)val y = User("Yana", 19.option)val users = Seq(m, b, y)println(users(0).name) !//плохоusers.headOption.foreach(println) !//хорошо

val f = users .filter(_.age.isDefined) .find(_.name +== "Matvey")

39Ïàòòåðí - ìàò÷èíã

• похоже на switch-case

39Ïàòòåðí - ìàò÷èíã

• похоже на switch-case

• мощнее

39Ïàòòåðí - ìàò÷èíã

• похоже на switch-case

• мощнее

• перебор возможных данных

39Ïàòòåðí - ìàò÷èíã

• похоже на switch-case

• мощнее

• перебор возможных данных

• всех!

39Ïàòòåðí - ìàò÷èíã

• похоже на switch-case

• мощнее

• перебор возможных данных

• всех!

• наглядно

39Ïàòòåðí - ìàò÷èíã

40

def printAge(user: User) = user.age match { case Some(a) #=> println(s"age is $a") case None #=> println("no age for user")}

41

def printAge(user: User) = user.age match { case Some(a) #=> println(s"age is $a") case None #=> println("no age for user")}

42

def printAge(user: User) = user.age match { case Some(a) #=> println(s"age is $a") case None #=> println("no age for user")}

43

def printAge(user: User) = user.age match { case Some(a) #=> println(s"age is $a") case None #=> println("no age for user")}

44

def printAge(user: User) = user.age match { case Some(14) #=> print("passport required") case Some(40) #=> print("change your passport")}

45

warning: match may not be exhaustive.It would fail on the following inputs: None, Some((x: Int forSome x not in (14, 40))) def printAge(user: User) = user.age match { ^

def printAge(user: User) = user.age match { case Some(14) #=> print("passport required") case Some(40) #=> print("change your passport")}

46

def printUser(user: User) = user match {

47

def printUser(user: User) = user match { case User("Matvey", _) #=> print("Welcome back")

48

def printUser(user: User) = user match { case User("Matvey", _) #=> print("Welcome back") case User(name, Some(age)) #=> print(s"You're at $age, mister $name")

49

def printUser(user: User) = user match { case User("Matvey", _) #=> print("Welcome back") case User(name, Some(age)) #=> print(s"You're at $age, mister $name") case User(name, None) #=> print(s"You have no age set, mister $name") }

50

def printUser(user: User) = user match { case User("Matvey", _) #=> print("Welcome back") case User(name, Some(age)) #=> print(s"You're at $age, mister $name") case User(name, None) #=> print(s"You have no age set, mister $name") }

!//all cases here, no warnings

51

val users = Seq(m, b, y)

52

val users = Seq(m, b, y)users.foreach { case User(_, Some(14)) #=> print("make a passport") case _ #=> print("don't worry")}

53Áåçîïàñíîñòü

• ошибиться сложно

53Áåçîïàñíîñòü

• ошибиться сложно

• никаких NPE

53Áåçîïàñíîñòü

• ошибиться сложно

• никаких NPE

• хорошая читаемость

53Áåçîïàñíîñòü

• ошибиться сложно

• никаких NPE

• хорошая читаемость

• куча операций

53Áåçîïàñíîñòü

2. Ðàçäåëÿé è âëàñòâóé

55

56trait Logger { val tag = getClass.getSimpleName def info(msg: String) = Log.info(tag, msg) }

57trait Logger { val tag = getClass.getSimpleName def info(msg: String) = Log.info(tag, msg) }

58trait Logger { val tag = getClass.getSimpleName def info(msg: String) = Log.info(tag, msg) }

59trait Logger { val tag = getClass.getSimpleName def info(msg: String) = Log.info(tag, msg) }class AwesomeFragment extends Fragment with Logger {}

60trait Logger { val tag = getClass.getSimpleName def info(msg: String) = Log.info(tag, msg) }class AwesomeFragment extends Fragment with Logger { info("constructor called")}

61trait Backstack { this: Fragment #=>

62trait Backstack { this: Fragment #=> def open(f: Fragment) = getChildFragmentManager .beginTransaction() .add(R.id.childRoot, f, null) .commit()}

63trait Backstack { this: Fragment #=> def open(f: Fragment) = getChildFragmentManager .beginTransaction() .add(R.id.childRoot, f, null) .commit()}

64trait Backstack { this: Fragment #=> def open(f: Fragment) = getChildFragmentManager .beginTransaction() .add(R.id.childRoot, f, null) .commit()}class AwesomeFragment extends Fragment with Logger with Backstack { info("constructor called") def onConfirmClick = open(new ConfirmFragment()) }

65trait Backstack { this: Fragment #=> def open(f: Fragment) = getChildFragmentManager .beginTransaction() .add(R.id.childRoot, f, null) .commit()}class AwesomeFragment extends Fragment with Logger with Backstack { info("constructor called") def onConfirmClick = open(new ConfirmFragment()) }

66

class ChatFragment extends Fragment with MessagesLoading with ChatItemsClicks with ChatActionBar with ChatMenus {

67

class ChatFragment extends Fragment with MessagesLoading with ChatItemsClicks with ChatActionBar with ChatMenus {

class RootActivity extends BaseActivity with Shaker with BackStack with DrawerHolder {

• модульность

• читаемость

• переиспользуемость

68Ðàçäåëÿé è âëàñòâóé

3. Èäåàëüíàÿ àðõèòåêòóðà

70×èñòûå ôóíêöèè

• уверенность в результате

70×èñòûå ôóíêöèè

• уверенность в результате

• легко тестировать

70×èñòûå ôóíêöèè

• уверенность в результате

• легко тестировать

• легко кешировать

70×èñòûå ôóíêöèè

• уверенность в результате

• легко тестировать

• легко кешировать

• легко переиспользовать

70×èñòûå ôóíêöèè

71Service aka Helper

• только бизнеслогика

71Service aka Helper

• только бизнеслогика

• определенная ее часть

71Service aka Helper

• только бизнеслогика

• определенная ее часть

• только чистые функции (>90%)

71Service aka Helper

• только бизнеслогика

• определенная ее часть

• только чистые функции (>90%)

• scala companion objects

71Service aka Helper

72

object MediaHelper { def extractFileFromUri(uri: Uri): Future[String] = … def makeMedia(id: Long, source: Source): Option[MediaDb] = … def saveFileToGallery(name: String, mt: MediaType): Future[String] = …

73

object MediaHelper { def extractFileFromUri(uri: Uri): Future[String] = … def makeMedia(id: Long, source: Source): Option[MediaDb] = … def saveFileToGallery(name: String, mt: MediaType): Future[String] = …

74

object ChatHelper { def updateChatName(chatId: Long, name: String): Unit = … def updateChatAvatar(chatId: Long, avatar: String): Unit = …

75

object ChatHelper { def updateChatName(chatId: Long, name: String): Unit = … def updateChatAvatar(chatId: Long, avatar: String): Unit = …

76

object ChatHelper { def updateChatName(chatId: Long, name: String): Unit = … def updateChatAvatar(chatId: Long, avatar: String): Unit = …

Publisher.ChatChanged.publish( ChatChangedEvent(chatId, name = newName) )

77

object ChatHelper { def updateChatName(chatId: Long, name: String): Unit = … def updateChatAvatar(chatId: Long, avatar: String): Unit = …

Publisher.ChatChanged.publish( ChatChangedEvent(chatId, name = newName) )

Publisher.ChatChanged.subscribe(onChatChanged) Publisher.ChatChanged.unsubscribe(onChatChanged)

78

object DbHelper extends DatabaseDsl with BaseDbHelper with KeyDbHelper with ChatDbHelper with MessageDbHelper with NoticeDbHelper with Logger {

79

object DbHelper extends DatabaseDsl with BaseDbHelper with KeyDbHelper with ChatDbHelper with MessageDbHelper with NoticeDbHelper with Logger {

80

object DbHelper extends DatabaseDsl with BaseDbHelper with KeyDbHelper with ChatDbHelper with MessageDbHelper with NoticeDbHelper with Logger {

81Èäåàëüíàÿ àðõèòåêòóðà

• расширяемость

81Èäåàëüíàÿ àðõèòåêòóðà

• расширяемость

• поддерживаемость

81Èäåàëüíàÿ àðõèòåêòóðà

• расширяемость

• поддерживаемость

• тестируемость

81Èäåàëüíàÿ àðõèòåêòóðà

• расширяемость

• поддерживаемость

• тестируемость

• отсутствие лишних действий

81Èäåàëüíàÿ àðõèòåêòóðà

4. Êðàñèâûå DSL è UI

83Êðàñèâûå DSL

• мини-язык для работы с определенное подсистемой

83Êðàñèâûå DSL

• мини-язык для работы с определенное подсистемой

• меньше кода

83Êðàñèâûå DSL

• мини-язык для работы с определенное подсистемой

• меньше кода

• надежнее

83Êðàñèâûå DSL

• мини-язык для работы с определенное подсистемой

• меньше кода

• надежнее

• лямбды лучше методов

83Êðàñèâûå DSL

• мини-язык для работы с определенное подсистемой

• меньше кода

• надежнее

• лямбды лучше методов

• особенно крут в UI

83Êðàñèâûå DSL

84TypedResources

• пришел с android-sbt-plugin

84TypedResources

• пришел с android-sbt-plugin

• сompile-time типизация айдишников

84TypedResources

• пришел с android-sbt-plugin

• сompile-time типизация айдишников

• жутко удобно

84TypedResources

• пришел с android-sbt-plugin

• сompile-time типизация айдишников

• жутко удобно

• безопасно

84TypedResources

• пришел с android-sbt-plugin

• сompile-time типизация айдишников

• жутко удобно

• безопасно

• приходится компилировать чаще

84TypedResources

85

TextView tv = (TextView) getView() .findViewById(R.id.title)

86

TextView tv = (TextView) getView() .findViewById(R.id.title)

val tv = getView.findViewById(R.id.title) .asInstanceOf[TextView]

87

TextView tv = (TextView) getView() .findViewById(R.id.title)

val tv = getView.findViewById(R.id.title) .asInstanceOf[TextView]

val tv = getView.find(TR.title)

88

TextView tv = (TextView) getView() .findViewById(R.id.title)

val tv = getView.findViewById(R.id.title) .asInstanceOf[TextView]

val tv = getView.find(TR.title)

89Ìåòîäû æèçíåííîãî öèêëà

• разрастаются

89Ìåòîäû æèçíåííîãî öèêëà

• разрастаются

• используются всегда

89Ìåòîäû æèçíåííîãî öèêëà

• разрастаются

• используются всегда

• содержат кучу ненужных слов

89Ìåòîäû æèçíåííîãî öèêëà

• разрастаются

• используются всегда

• содержат кучу ненужных слов

90Ìåòîäû æèçíåííîãî öèêëà

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); !//код здесь}

• разрастаются

• используются всегда

• содержат кучу ненужных слов

91Ìåòîäû æèçíåííîãî öèêëà

@Overridepublic void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); !//код здесь}

92trait HostedFragment extends Fragment with Logger {

93trait HostedFragment extends Fragment with Logger { protected val onCreateBodies = new ArrayBuffer[() #=> Unit]

94trait HostedFragment extends Fragment with Logger { protected val onCreateBodies = new ArrayBuffer[() #=> Unit]

95trait HostedFragment extends Fragment with Logger { protected val onCreateBodies = new ArrayBuffer[() #=> Unit] def onCreate(body: #=> Any): Unit = { onCreateBodies += { () #=> body () } }

96trait HostedFragment extends Fragment with Logger { protected val onCreateBodies = new ArrayBuffer[() #=> Unit] def onCreate(body: #=> Any): Unit = { onCreateBodies += { () #=> body () } }

97trait HostedFragment extends Fragment with Logger { protected val onCreateBodies = new ArrayBuffer[() #=> Unit] def onCreate(body: #=> Any): Unit = { onCreateBodies += { () #=> body () } } override def onCreate(savedInstanceState: Bundle): Unit = { super.onCreate(savedInstanceState) onCreateBodies.foreach(_ ()) }

98

class AwesomeFragment extends HostedFragment { onCreate { !//do some stuff }

99

class AwesomeFragment extends HostedFragment { onCreate { !//do some stuff } onCreate { !//do more stuff }

100

class AwesomeFragment extends HostedFragment { onCreate { !//do some stuff } onCreate { !//do more stuff } onViewCreated { getView.find(TR.title).setText("I love Scala") }

101trait BackStackApi { def clear(): Unit def removeLast(): Unit def last: Option[HostedFragment] def removeUntil[F <: HostedFragment]: Boolean

102trait BackStackApi { def clear(): Unit def removeLast(): Unit def last: Option[HostedFragment] def removeUntil[F <: HostedFragment]: Boolean

removeUntil[ChatFragment]

103package object

• глобальная видимость

103package object

• глобальная видимость

• внутри пакета

103package object

• глобальная видимость

• внутри пакета

• содержит переменные и функции

103package object

104

implicit class RichView[V <: View](view: V) {

105

implicit class RichView[V <: View](view: V) {

106

implicit class RichView[V <: View](view: V) {

107

implicit class RichView[V <: View](view: V) {

108

implicit class RichView[V <: View](view: V) { def onClick[U](f: #=> U): Unit = { view.setOnClickListener(new View.OnClickListener { def onClick(p: View): Unit = f }) }

109

implicit class RichView[V <: View](view: V) { def onClick[U](f: #=> U): Unit = { view.setOnClickListener(new View.OnClickListener { def onClick(p: View): Unit = f }) }

110

val tv = getView.find(TR.title)

111

val tv = getView.find(TR.title) new RichView(tv).onClick(findLastUnread)

112

val tv = getView.find(TR.title) new RichView(tv).onClick(findLastUnread) !//не очень

113

val tv = getView.find(TR.title) new RichView(tv).onClick(findLastUnread) !//не очень

tv.onClick(findLastUnread) !// очень даже

114

val tv = getView.find(TR.title) new RichView(tv).onClick(findLastUnread) !//не очень

tv.onClick(findLastUnread) !// очень даже

editText.afterTextChanged { text: String #=> info(text)}

115

@inline def dp(value: Float): Int = math.round(ctx.getResources.getDisplayMetrics.density * value)

116

@inline def dp(value: Float): Int = math.round(ctx.getResources.getDisplayMetrics.density * value) @inline def color(resId: Int): Int = ctx.getResources.getColor(resId)

117

@inline def dp(value: Float): Int = math.round(ctx.getResources.getDisplayMetrics.density * value) @inline def color(resId: Int): Int = ctx.getResources.getColor(resId) @inline def string(resId: Int): String = ctx.getString(resId)

118

val str = string(R.string.APP_VERSION)

119

val str = string(R.string.APP_VERSION) val title = getView.find(TR.title)

120

val str = string(R.string.APP_VERSION) val title = getView.find(TR.title)title.setCompoundDrawablePadding(dp(60))

121

val str = string(R.string.APP_VERSION) val title = getView.find(TR.title)title.setCompoundDrawablePadding(dp(60)) title.setTextColor(color(R.color.red))

Êðàñîòà

5. Íåïðèÿòíîñòè

124Íåïðèÿòíîñòè

• количество методов

124Íåïðèÿòíîñòè

• количество методов

• сразу +15к

124Íåïðèÿòíîñòè

• количество методов

• сразу +15к

• долгая сборка

124Íåïðèÿòíîñòè

• количество методов

• сразу +15к

• долгая сборка

• 120 секунд

124Íåïðèÿòíîñòè

• количество методов

• сразу +15к

• долгая сборка

• 120 секунд

• dalvik и ART не любят Scala

124Íåïðèÿòíîñòè

• количество методов

• сразу +15к

• долгая сборка

• 120 секунд

• dalvik и ART не любят Scala

• возможность выстрелить в ногу из-за незнания Scala

124Íåïðèÿòíîñòè

• количество методов

• сразу +15к

• долгая сборка

• 120 секунд

• dalvik и ART не любят Scala

• возможность выстрелить в ногу из-за незнания Scala

• поиск сотрудников

124Íåïðèÿòíîñòè

×òî â èòîãå?

126Èòîãè

• мощнейший инструмент

126Èòîãè

• мощнейший инструмент

• требует знаний и умений

126Èòîãè

• мощнейший инструмент

• требует знаний и умений

• позволяет писать восхитительный DSL

126Èòîãè

• мощнейший инструмент

• требует знаний и умений

• позволяет писать восхитительный DSL

• надежный

126Èòîãè

• мощнейший инструмент

• требует знаний и умений

• позволяет писать восхитительный DSL

• надежный

• очень много плюшек при правильном использовании

126Èòîãè

• мощнейший инструмент

• требует знаний и умений

• позволяет писать восхитительный DSL

• надежный

• очень много плюшек при правильном использовании

• долгое время компиляции

126Èòîãè

• мощнейший инструмент

• требует знаний и умений

• позволяет писать восхитительный DSL

• надежный

• очень много плюшек при правильном использовании

• долгое время компиляции

• сложный поиск команды

126Èòîãè

Android ðàçðàáîòêà ìîæåò áûòü ïðèÿòíîé

Ïîïðîáóéòå Scala è âàì íå çàõî÷åòñÿ îáðàòíî

129

Ìàëüêîâ Ìàòâåé

Ñïàñèáî

matveyka_jj

H102RPT4

Q & A