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

Scala for Android Explained

Embed Size (px)

Citation preview

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