scala äëÿ android. Ìèô èëè -...
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