16 советов по разработке для андроид на языке Kotlin. Часть 2

Всем привет. В преддверии старта базового курса по Android-разработке, продолжаем делиться полезным материалом.

txpcwfnd2fbjr6qqwcbmtajt9ys.png

Перед прочтением этих советов вам желательно ознакомиться с документацией 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, вы можете изменить ее в зависимости от цели, метода и его способности быть измененным.

© Habrahabr.ru