16 советов по разработке для андроид на языке Kotlin. Часть 2
Всем привет. В преддверии старта базового курса по Android-разработке, продолжаем делиться полезным материалом.
Перед прочтением этих советов вам желательно ознакомиться с документацией Kotlin и самостоятельно изучить язык на сайте try.kotlinlang.org
. Поскольку эти советы направлены именно на использование Kotlin в контексте разработки под Android, у вас также должен быть опыт работы с Android SDK. Также желательно ознакомиться с плагином Kotlin и использованием Kotlin с Android Studio от JetBrains (создателей Kotlin)
Читать первую часть
Описание объектов
Описания объектов допускают только синглетоны, которые нельзя принять за класс с возможностью создания экземпляров. Поэтому вам не нужно хранить синглеты как переменные статического класса или в классе Application.
Например, если у меня есть служебный класс со статическими методами, связанными с потоками, а я хочу получить доступ ко всему приложению, можно сделать вот так:
package com.myapps.example.util
import android.os.Handler
import android.os.Looper
// помните, что это объект, а не класс
object ThreadUtil {
fun onMainThread(runnable: Runnable) {
val mainHandler = Handler(Looper.getMainLooper())
mainHandler.post(runnable)
}
}
ThreadUtil можно вызвать позже таким же способом, как и при вызове метода статического класса:
ThreadUtil.onMainThread(runnable)
Это означает, что больше не нужно имитировать поведение статического класса при помощи закрытого конструктора и не нужно выяснять, где хранится экземпляр. Объекты, по сути, являются первоначальными элементами языка. По этому же принципу мы создаем объекты вместо внутренних классов:
v
iewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {
bindUser(position)
}
});
Оба по сути делают одно и то же — создают один экземпляр класса как объявленный объект.
Вспомогательные объекты
На первый взгляд в Kotlin нет статических переменных и методов. В данном языке отсутствуют эти концепции, зато есть концепция вспомогательных объектов. Они являются одноэлементными объектами в классе, содержащими методы и переменные, к которым вы можете обращаться статическим способом. Сопутствующий объект допускает определенные константы и методы, подобные статическим классам в Java. С его помощью вы можете следовать шаблону фрагментов newInstance.
Взгляните на сопутствующий объект в его простейшей форме:
class User {
companion object {
const val DEFAULT_USER_AGE = 30
}
}
// к нему можно обратиться позже так:
user.age = User.DEFAULT_USER_AGE
В Android мы обычно используем статические методы и переменные для создания статических фабрик для фрагментов. Например:
class ViewUserActivity : AppCompatActivity() {
companion object {
const val KEY_USER = "user"
fun intent(context: Context, user: User): Intent {
val intent = Intent(context, ViewUserActivity::class.java)
intent.putExtra(KEY_USER, user)
return intent
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_cooking)
val user = intent.getParcelableExtra(KEY_USER)
//...
}
}
Создание Intent похоже на аналогичное действие в Java:
val intent = ViewUserActivity.intent(context, user)
startActivity(intent)
Этот паттерн хорош, так как он уменьшает вероятность того, что у Intent или Fragment будут отсутствовать необходимые для отображения пользовательского или любого другого контента данные. Сопутствующие объекты — это способ сохранить форму статического доступа в Kotlin, и их следует использовать как компенсацию отсутствия классов.
Глобальные константы
Kotlin позволяет вам определять константы, которые видны в одном месте приложения (если это применимо). Но область действия констант по возможности должна быть максимально уменьшена. А в ситуациях, когда нужно, чтобы область была глобальной, в Kotlin есть отличный способ сделать это без необходимости использовать класс констант. Что-то типа:
package com.myapps.example
import android.support.annotation.StringDef
// Это не класс, это объект!
const val PRESENTATION_MODE_PRESENTING = "presenting"
const val PRESENTATION_MODE_EDITING = "editing"
Потом их можно использовать как константы в любом месте проекта:
import com.savvyapps.example.PRESENTATION_MODE_EDITING
val currentPresentationMode = PRESENTATION_MODE_EDITING
Констант в проекте должно быть как можно меньше, чтобы уменьшить его сложность. Если у вас есть значение, которое относится только к пользовательскому классу, лучше поместить его во вспомогательный объект.
Расширения
Расширения полезны, потому что они позволяют добавлять функциональность класса, не наследуя его. Например, как добавить к Activity какой-нибудь метод, типа hideKeyboard()
? С помощью расширений можно легко это сделать:
fun Activity.hideKeyboard(): Boolean {
val view = currentFocus
view?.let {
val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
return inputMethodManager.hideSoftInputFromWindow(view.windowToken,
InputMethodManager.HIDE_NOT_ALWAYS)
}
return false
}
Расширения полезны тем, что они:
- помогают улучшить читабельность кода;
- избавляют от необходимости создавать служебные классы и методы.
Можно пойти дальше и улучшить архитектуру кода. Представьте, что у вас есть базовая модель, например, Article, которая рассматривается как класс данных, извлеченных из источника по API:
class Article(val title: String, val numberOfViews: Int, val topic: String)
Нужно определить релевантность Article для пользователя на основе какой-то формулы. Должны ли вы поместить её непосредственно в класс Article? И если модель должна содержать только данные из API, не более того, снова можно использовать расширения:
fun Article.isArticleRelevant(user: User): Boolean {
return user.favoriteTopics.contains(topic)
}
В настоящее время это простая возможность проверки присутствия темы Article в списке любимых тем пользователя.
Эта логика может меняться в зависимости от того, где вы хотите проверить эти и другие атрибуты пользовательского поведения. Поскольку эта логика поддерживается в некоторой степени независимо от модели Article, вы можете изменить ее в зависимости от цели, метода и его способности быть измененным.