简体   繁体   English

从 MvvM 架构和 Kotlin 中的广播接收器更新片段 ui

[英]Update fragment ui from broadcast receiver in MvvM architecture and Kotlin

Being new to kotlin and mvvm architecture I wasted the past two days searching for an answer for this problem without any success.作为 kotlin 和 mvvm 架构的新手,我浪费了过去两天来寻找这个问题的答案,但没有任何成功。 So I am searching for a clean solution to change the fragment ui (in my case it is just a textview) when the device is connected/disconnected in the internet.所以我正在寻找一个干净的解决方案来更改片段 ui(在我的情况下它只是一个文本视图),当设备在互联网上连接/断开时。 I am using the mvvm architecture and the android architecture component(viewmodels, livedata, data bindings...).我正在使用 mvvm 架构和 android 架构组件(视图模型、实时数据、数据绑定......)。 I have a base activity with a bottom navigation and multiple fragments.我有一个带有底部导航和多个片段的基本活动。

I am trying to get the connectivity event from a custom broadcast receiver and I want somehow to pass that event in the viewmodel or my fragment.我正在尝试从自定义广播接收器获取连接事件,并且我想以某种方式在视图模型或我的片段中传递该事件。

My first thought was to use an interface.我的第一个想法是使用接口。 This interface is triggered by the onreceive of the broadcastreceiver and it is implemented in my fragment or viewmodel, so when the internet event happens, the textview is updated in my fragment.此接口由 broadcastreceiver 的 onreceive 触发,并在我的片段或视图模型中实现,因此当互联网事件发生时,textview 在我的片段中更新。 But I am not sure about how to use the interface in the broadcast receiver, or I don't know even if this is possible or a good practice.但是我不确定如何在广播接收器中使用该接口,或者我不知道即使这是可能的还是一个好的做法。

what I did till now is create the broadcast receiver到目前为止我所做的是创建广播接收器

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

register/unregister it in my base activity在我的基本活动中注册/注销它

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

(I know that ConnectivityManager.CONNECTIVITY_ACTION is deprecated but I couldn't find a better solution). (我知道 ConnectivityManager.CONNECTIVITY_ACTION 已被弃用,但我找不到更好的解决方案)。

create an interface创建接口

interface ConnectionStatusChange{
    fun onChange()
}

and implement the interface in my Fragment并在我的片段中实现接口

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

I would like to know if this approach is a good practice and how can I make it work.我想知道这种方法是否是一种好的做法,以及如何使其发挥作用。 In the case that it is not possible to be done like that then please give me some suggestions (code always appreciated. =)), Also it would be good to have a modern.如果无法做到这一点,请给我一些建议(代码总是很感激。=)),另外最好有一个现代的。 rather than a 5 years old solution.而不是 5 年前的解决方案。 Thank you in advance.先感谢您。

Eventually I found a clean and modern solution like I wanted.最终我找到了一个我想要的干净和现代的解决方案。 The trick is to wrap the BroadcastReceiver in a LiveData.诀窍是将 BroadcastReceiver 包装在 LiveData 中。 By using this pattern you can observe the connection LiveDada directly from your fragment and update the ui.通过使用此模式,您可以直接从片段中观察连接 LiveDada 并更新 ui。 Of Course it is also possible (like sergiy tikhonov mentioned) to have a companion object in the base activity with a LiveData that is getting updated from the BroadcastReceiver and you can observe it from the fragment, but I didn't like this approach.当然,也有可能(就像提到的 sergiy tikhonov 一样)在基础活动中拥有一个伴随的 object,其中的 LiveData 是从 BroadcastReceiver 更新的,您可以从片段中观察它,但我不喜欢这种方法。

So it works like this:所以它是这样工作的:

Create an enum with the states of the network.创建一个包含网络状态的枚举。

enum class NetworkAvailability {
    UNKNOWN,
    CONNECTED,
    DISCONNECTED
}

Create the class that extends the LiveData.创建扩展 LiveData 的 class。 I am using the singleton pattern because I don't want multiple instances.我正在使用 singleton 模式,因为我不想要多个实例。 I create a BroadcastReceiver instance and I register/unregister it in onActive and onInactive methods of the LiveData.我创建了一个 BroadcastReceiver 实例,并在 LiveData 的 onActive 和 onInactive 方法中注册/取消注册它。

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)
    }
}

And finally I observe the LiveData in my fragment.最后,我观察了片段中的 LiveData。

ConnectionStateLiveData.get(context).observe(viewLifecycleOwner, Observer {
                if (it == NetworkAvailability.CONNECTED) {
                    binding.noInternetTextView.visibility = View.GONE
                }
            })
  1. base/helper class底座/辅助 class
public class ReceiverLiveData<T> extends LiveData<T> {

    private final Context context;
    private final IntentFilter filter;
    private final BiFunction<Context, Intent, T> mapFunc;


    public ReceiverLiveData(Context context, IntentFilter filter, BiFunction<Context, Intent, T> mapFunc) {
        this.context = context;
        this.filter = filter;
        this.mapFunc = mapFunc;
    }

    @Override
    protected void onInactive() {
        super.onInactive();
        context.unregisterReceiver(mBroadcastReceiver);
    }

    @Override
    protected void onActive() {
        super.onActive();
        //initialize LiveData value
        mBroadcastReceiver.onReceive(context, null);
        context.registerReceiver(mBroadcastReceiver, filter);
    }

    final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            setValue(mapFunc.apply(context, intent));
        }
    };
}
  1. ViewModel class查看型号 class
    public static class ViewModel extends AndroidViewModel {
        public ViewModel(@NonNull Application application) {
            super(application);
        }

        public final LiveData<NetworkInfo> activeNetworkInfoLiveData = new ReceiverLiveData<>(getApplication(), new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION), (c, i) -> ((ConnectivityManager) c.getSystemService(CONNECTIVITY_SERVICE)).getActiveNetworkInfo());

    }
  1. in databinding layout file在数据绑定布局文件中
<?xml version="1.0" encoding="utf-8"?>
<layout>

    <data>

        <variable
            name="viewModel"
            type="com.XXXX.settingitemtest.MainActivity.ViewModel" />

    </data>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text='@{viewModel.activeNetworkInfoLiveData.connected ? "Connected" : "Disconnected"}'/>


    </LinearLayout>
</layout>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM