Обновление пользовательского интерфейса фрагмента от приемника вещания в архитектуре MvvM и Kotlin - PullRequest
0 голосов
/ 15 апреля 2020

Будучи новичком в kotlin и архитектуре mvvm, я потратил последние два дня, пытаясь найти решение этой проблемы, но безуспешно. Поэтому я ищу чистое решение для изменения фрагмента пользовательского интерфейса (в моем случае это просто просмотр текста), когда устройство подключено / отключено в inte rnet. Я использую архитектуру mvvm и компонент архитектуры android (viewmodels, livingata, привязки данных ...). У меня есть базовое действие с навигацией снизу и несколькими фрагментами.

Я пытаюсь получить событие подключения от пользовательского приемника широковещания и хочу каким-то образом передать это событие в модель представления или в мой фрагмент.

Моей первой мыслью было использование интерфейса. Этот интерфейс запускается при получении вещательного приемника, и он реализован в моем фрагменте или модели представления, поэтому, когда происходит событие inte rnet, текстовое представление обновляется в моем фрагменте. Но я не уверен, как использовать интерфейс в приемнике вещания, или я не знаю, даже если это возможно или хорошая практика.

что я до сих пор делал, это создавал широковещательный приемник

class ConnectionStateObserver: BroadcastReceiver() {
    override fun onReceive(p0: Context?, p1: Intent?) {
       //somehow use the interface in here
    }
}

регистрировал / отменял регистрацию в моей базовой активности

    override fun onResume() {
        super.onResume()
        val broadcastIntent = IntentFilter()
        broadcastIntent.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
        registerReceiver(ConnectionStateObserver(), broadcastIntent)
    }
    override fun onPause() {
        super.onPause()
        unregisterReceiver(ConnectionStateObserver())
    }

(я знаю, что ConnectivityManager.CONNECTIVITY_ACTION устарел, но я не мог найти лучшее решение).

создать интерфейс

interface ConnectionStatusChange{
    fun onChange()
}

и реализовать интерфейс в моем фрагменте

class myFragment : Fragment(), ConnectionStatusChange {
override fun onChange() {
        // do the ui changes here
    }
}

Я хотел бы знать, является ли этот подход хорошей практикой и как я могу сделай так, чтоб это работало. В случае, если это невозможно сделать, пожалуйста, дайте мне несколько советов (код всегда приветствуется! =)). Также было бы хорошо иметь современное, а не 5-летнее решение. Заранее спасибо.

1 Ответ

0 голосов
/ 16 апреля 2020

В конце концов я нашел чистое и современное решение, как я хотел. Хитрость заключается в том, чтобы обернуть BroadcastReceiver в LiveData. Используя этот шаблон, вы можете наблюдать за подключением LiveDada непосредственно к вашему фрагменту и обновлять интерфейс. Конечно, также возможно (как упомянул Сергей Тихонов) иметь сопутствующий объект в базовом упражнении с LiveData, который обновляется из BroadcastReceiver, и вы можете наблюдать его из фрагмента, но мне не понравился этот подход.

Так что это работает так:

Создайте перечисление с состояниями сети.

enum class NetworkAvailability {
    UNKNOWN,
    CONNECTED,
    DISCONNECTED
}

Создайте класс, расширяющий LiveData. Я использую шаблон синглтона, потому что я не хочу нескольких экземпляров. Я создаю экземпляр BroadcastReceiver и регистрирую / отменяю его регистрацию в методах onActive и onInactive в LiveData.

class ConnectionStateLiveData(val context: Context) : LiveData<NetworkAvailability>() {

    companion object {
        private lateinit var instance: ConnectionStateLiveData

        fun get(context: Context): ConnectionStateLiveData {
            instance = if (::instance.isInitialized){
                instance
            }else{
                ConnectionStateLiveData(context)
            }
            return instance
        }
    }

    private val connectivityBroadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(p0: Context?, p1: Intent?) {
            val connectivityManager =
                context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
            val netInfo = connectivityManager.activeNetworkInfo

            value = if (netInfo != null && netInfo.isConnected) {
                NetworkAvailability.CONNECTED
            } else {
                NetworkAvailability.DISCONNECTED
            }
        }
    }

    override fun onActive() {
        super.onActive()
        value = NetworkAvailability.UNKNOWN
        val broadcastIntent = IntentFilter()
        broadcastIntent.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
        context.registerReceiver(connectivityBroadcastReceiver, broadcastIntent)
    }

    override fun onInactive() {
        super.onInactive()
        context.unregisterReceiver(connectivityBroadcastReceiver)
    }
}

И, наконец, наблюдаю данные LiveData в своем фрагменте.

ConnectionStateLiveData.get(context).observe(viewLifecycleOwner, Observer {
                if (it == NetworkAvailability.CONNECTED) {
                    binding.noInternetTextView.visibility = View.GONE
                }
            })
...