Пишем на Kotlin под Android

в 9:52, , рубрики: android, gradle, intellij idea, kotlin, Блог компании JetBrains, Программирование, Разработка под android

Года два назад я обнаружил, что в Android Market нет удобных мне приложений для учета времени, затраченного на разные проекты. Как настоящий лентяй я решил не искать требуемое, а написать его сам. Это оказалось несколько сложнее, чем казалось вначале, и я все забросил в режиме вялотекущего хобби воплощение моего желания прожило больше года, медленно продвигаясь от идеи к альфа-версии.

И тут вдруг добрые люди предложили мне рассказать, как писать под Android на Kotlin'e. Мне показалось, что это — добрый знак, и я быстро переписал приложение на Kotlin, закончил его и выложил в Google Play.

Важное уточнение: эта статья — только про то, как писать под Android на Kotlin'e. Интересно будет только тем, кто хочет попробовать Kotlin, и тем, кто хочет понять, зачем его пробовать. Приложение, которое я написал — очень-очень простое (и само по себе никак не относится к JetBrains). В его коде гарантированно можно что-то улучшить, оно выложено на Github только для примера, а не в качестве образца восхитительного кода. Оно работает, и если вам хочется его попробовать, то поставить можно отсюда. Но еще раз: это — демо, его цель — быть не совершенным приложением, а введением в Kotlin под Андроид.

Опять что-то новое. И зачем мне это знать?

Пишем на Kotlin под Android

Чтоб не оттягивать самое интересное, упомянем только самые главные преимущества Kotlin'a:

  • компактность кода. Быстро писать, легко читать, меньше наведенных ошибок;
  • встроенная защита от ошибок, прежде всего — nullability, т.е. требование к программисту явно указывать, что некая переменная может принимать значение null;
  • легкость в освоении (по-моему, даже начинающий разработчик на Java освоит Kotlin без труда).

На Kotlin'e можно писать значительно быстрее, чем на Jav'e, и код, на мой взгляд, получается более красивым.

Кстати, на github'e уже более 200 репозиториев с кодом на Kotlin'e, а если говорить про мобильные приложения, то Kotlin использовался при разработке мессенджера Telegram.

Ну ладно, а с чего начать-то?

Для работы потребуется IDE. Я по очевидным причинам предпочитаю IntelliJ IDEA, однако JetBrains делает плагин Kotlin и для Eclipse, а любители vim могут пользоваться отдельным компилятором языка.

Дальше будем предполагать, что все, кто пожелает попробовать Kotlin в деле под Android, будут использовать IntelliJ IDEA. Для работы понадобится установить плагин Kotlin и позаботиться, чтобы на компьютере были gradle и Android SDK (это к Kotlin'y отношения не имеет, это надо для разработки под Android на чем угодно).

Вначале была Java

У меня уже было частично написанное приложение, когда я решил довести его до конца. Все было написано на Java.

Суть приложения: на экране есть список красных задач, если кликнуть по задаче, она зеленеет, и время пошло: это значит, что пользователь работает над задачей. Как прекратил — кликает по ней еще раз, и задача снова краснеет, показывая, как немного времени он на нее потратил. Приложение слепо доверяет пользователю, не проверяя его никак: это вам не «большой брат смотрит за тобой» от oDesk'a.

То, что было написано на Java, я хотел переписать на Kotlin, душа просила кода на 100% на Kotlin, несмотря на то, что в одном проекте можно использовать код на Java и на Kotlin'e одновременно. Оказалось, что с унаследованным кодом все просто: рядом с src/main/java создаем папку src/main/kotlin, и в нее помещаем классы. Файлы в Kotlin'е заканчиваются не на .java, а на .kt. Тут к месту оказался приятный момент: Kotlin не требует соответствия «один класс — один файл». Можно в один файл запихнуть столько классов, сколько хочется по логике приложения. В моем приложении было всего две логических части — работа с базой данных и экранный интерфейс пользователя, так что количество файлов можно было сократить.

Плагин Kotlin к IntelliJ IDEA умеет конвертировать файлы .java в файлы .kt, выполняя аккуратную трансляцию из Java в Kotlin. Можно это делать через правый клик по файлу и контекстное меню, а можно — прямо при копировании кода на Java в файл .kt (плагин спросит, не сконвертировать ли).

Пишем код на Kotlin'e

Код на языке Kotlin компактен: например, класс вместе с конструктором и getters/setters описывается так:

class Item (val project: String, var status: String, var secondsSpent: Long, var lastactivated: Long)

Посмотрим на код, который создает диалог для ввода текста по клику на кнопку.

Java:

addButton.setOnClickListener(new OnClickListener() {
  public void onClick(View v) { 
    AlertDialog.Builder alert = new AlertDialog.Builder (this);
    alert.setPositiveButton("OK", new DialogInterface.OnClickListener()  {
       public void onClick(DialogInterface dialog, int which)  {
            result = input.getText().toString()
       }      
    });  
  }  
} 

Kotlin:

addButton.setOnClickListener() {
      var alert = AlertDialog.Builder(this);
      alert.setPositiveButton("ОК") {
         dialog, whichButton ->  result = input.getText().toString()}
      }
 }

Согласитесь, код на Kotlin'e читается проще. Для внимательного читателя заметим, что это, конечно, только фрагмент кода, так как при создании диалога нужна не только кнопка «OK», но и кнопка «Отменить». Полностью код выложен на Github.

Что дает возможность так сокращать код? Например, в Kotlin возможна такая форма записи: если где-то в качестве параметра ожидается экземпляр класса с одним абстрактным методом, туда можно просто передать лямбду: именно это и показано в примере выше.

Обратите внимание, что в Kotlin'e можно не указывать тип переменной, так как он будет выведен из правой части присваивания:

Java Kotlin
AlertDialog.Builder alert = new AlertDialog.Builder (this); val alert = AlertDialog.Builder(this)

Интересная особенность синтаксиса в Kotlin'e: если в функцию передаются параметры, и последний параметр — функция, то ее можно вынести за скобки. Именно это мы видим во фрагменте, где в setPositiveButton передается лямбда, которая сработает по нажатию кнопки «ОК»:

alert.setPositiveButton("ОК") { dialog, whichButton ->  result = input.getText().toString() }

То же самое можно записать как

alert.setPositiveButton("ОК", { dialog, whichButton ->  result = input.getText().toString() } )

Вы вольны сами выбрать, какой вариант кажется проще для чтения.

Использование библиотек на Java в коде на Kotlin'e

Еще один приятный момент — возможность напрямую использовать библиотеки, написанные на Java, из кода на Kotlin.

Например, чтобы нормально обрабатывать спецсимволы и Unicode в названиях проектов при сохранении их в SQLite, я использовал функции StringEscapeUtils.escapeJava и StringEscapeUtils.unescapeJava из популярной библиотеки, которую достаточно импортировать оператором import:

import org.apache.commons.lang3.StringEscapeUtils

Возвращаемое значение

В Kotlin лямдба-функция не требует оператора return, так как возвращает значение, вычисленное в последнем выражении. Так, в вызове setOnTouchListener ожидается, что последний параметр вызова — функция, возвращающая boolean. Это значение фактически возвращает gestureDetector.onTouchEvent(aEvent).

Java:

gestureListener = new View.OnTouchListener() {
       public boolean onTouch(View v, MotionEvent event) {
             return gestureDetector.onTouchEvent(event);
       }
};

Kotlin:

getListView()?.setOnTouchListener() { v, aEvent -> gestureDetector.onTouchEvent(aEvent) }

В коде лямбды на Kotlin не надо писать return gestureDetector.onTouchEvent(aEvent), результат вызова gestureDetector.onTouchEvent(aEvent) и так будет возвращен по умолчанию.

Аналогично работает возврат значений функциями, определенными через знак "=", вот так:

override fun getCount() = data.size()

Строковые шаблоны

Чтобы избавиться от длинных строк, трудных для чтения и дающих неоптимальный байт-код, в Kotlin'e применены строковые шаблоны (string templates):

setMessage("Удаляем $projectToDelete?")

Здесь projectToDelete — это строковая переменная. Синтаксис такого шаблона привычен любому, кто имел дело с переменными среды в UNIX, например. Особенно удобны шаблоны для строк, составленных из текста и значений многих переменных, да еще и с форматированием:

secondsSpent.setText("${format("%02d", hours)}:${format("%02d",minutes)}:${format("%02d", seconds)}")

Между прочим, с форматированием по ходу дела вышла интересная история: тип String в Kotlin — свой, и метода format в нем нет, поэтому пришлось импортировать java.lang.String.format явным образом и потом к нему так обращаться. Удивительно, кстати, что ни в Java, ни в Kotlin'e до сих пор нет метода secondsToHumanReadableString, преобразующего целое число секунд в строку формата

ЧЧ:ММ:СС.

Операторы when и with

Очень удобно использовать when для множественного выбора и with для сокращения обращений к методам и свойствам — и того, и другого хватает даже в маленьком приложении под Android. В моем случае это давало более легкий в чтении код, например:

with (alert) {
   setPositiveButton("ОК") {
      dialog, whichButton ->
      ... тут код обработчика нажатия ОК ...
   }
   setNegativeButton("Отмена") { dialog, whichButton -> }
// по "Отмене" делать ничего и не надо
   create()
   show()
}

Без with получились бы alert.setPositiveButton, alert.setNegativeButton, alert.create, alert.show. Оператор when тоже повышает читаемость:

when (item.status) {
   "active" -> {
   item.status = "inactive"
   ... тут еще всякие важные штуки ...
   }
   "inactive" -> {
   item.status = "active"
   ... тут еще тоже всякое ...
   }
}

Компиляция с помощью Gradle

Если вы создаете build.gradle руками и/или собираете проект gradle'ом без IDE, то прочтите, как сдружить gradle, Kotlin и Android на сайте Kotlinlang.org.

Не знаю, есть ли такая проблема у пользователей Android Studio, но у привычного к IntelliJ IDEA человека работа с Gradle может вызвать вопрос, как собрать .apk для релиза, а не для отладки.

Для этого в IDEA есть плагин Gradle, открывается кликом по табу справа:

Пишем на Kotlin под Android

По умолчанию IDEA собирает *-debug-unsigned.apk, т.е. то, что в Google Play не положишь. Чтобы оно стало *-release-signed.apk, надо сгенерировать ключ для подписи, положить его в keystore, и вписать несколько строк в build.gradle вашего проекта, чтобы Gradle знал, где ключ взять. Для сборки release, выберите в окне Gradle задачу assembleRelease двойным кликом по ней.

Про то, как создавать ключ и подписывать приложение вы либо уже знаете, либо можете прочесть подробнее на stackoverflow.

На заметку

Недавно открылся наш новый сайт про Kotlin kotlinlang.org, который хостится на Jekyll+Github. Чем это решение хорошо, на Хабре недавно уже писали.

Если в статье какие-то примеры кода показались вам красивыми, то это — несомненная заслуга Натальи Ухорской, которая работает у нас в команде Kotlin. Я очень благодарен Наташе за советы, без которых эта статья оказалась бы короче, а код — менее похожим на классический Kotlin.

Мы будем очень рады, если сэкономленные за счет использования Kotlin часы вы сможете провести на пляже. Хорошего остатка лета!

Автор: philipto

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js