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)

    Это означает, что больше не нужно имитировать поведение статического класса при помощи закрытого конструктора и не нужно выяснять, где хранится экземпляр. Объекты, по сути, являются первоначальными элементами языка. По этому же принципу мы создаем объекты вместо внутренних классов:

    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<User>(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, вы можете изменить ее в зависимости от цели, метода и его способности быть измененным.
    OTUS. Онлайн-образование
    564,81
    Цифровые навыки от ведущих экспертов
    Поделиться публикацией

    Комментарии 0

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

    Самое читаемое