【问题标题】:Update fragment ui from broadcast receiver in MvvM architecture and Kotlin从 MvvM 架构和 Kotlin 中的广播接收器更新片段 ui
【发布时间】:2020-07-28 07:09:20
【问题描述】:

作为 kotlin 和 mvvm 架构的新手,我浪费了过去两天来寻找这个问题的答案,但没有任何成功。所以我正在寻找一个干净的解决方案来更改片段 ui(在我的情况下它只是一个文本视图),当设备在互联网上连接/断开时。我正在使用 mvvm 架构和 android 架构组件(viewmodels、livedata、数据绑定......)。我有一个带有底部导航和多个片段的基本活动。

我正在尝试从自定义广播接收器获取连接事件,并且我想以某种方式在视图模型或我的片段中传递该事件。

我的第一个想法是使用界面。此接口由广播接收器的 onreceive 触发,并在我的片段或视图模型中实现,因此当互联网事件发生时,文本视图在我的片段中更新。但是我不确定如何在广播接收器中使用该接口,或者我不知道这是否可行或一个好的做法。

到目前为止我所做的是创建广播接收器

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

并在我的 Fragment 中实现接口

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

我想知道这种方法是否是一种好的做法,以及如何使其发挥作用。如果不可能这样做,请给我一些建议(代码总是很感激!=))。此外,最好有一个现代的,而不是 5 年前的解决方案。提前谢谢你。

【问题讨论】:

  • 尝试使用观察者模式。如果您的堆栈中有 android 架构组件,则可以使用 LiveData。或者 RxJava 作为替代。例如,您可以保存应用程序级别的 MutableLiveData 值,在 BroadcastReceiver 中更改它并观察 Fragments 中的更改。
  • 如果我的片段视图模型中有一个 MutableLiveData 变量,我如何从 BroadcastReceiver 访问它,因为我在 BroadcastReceiver 中没有视图模型的实例?
  • 我讲述了应用程序级别的 MutableLiveData(自定义应用程序类中的字段),而不是片段 ViewModel。

标签: android android-fragments kotlin mvvm broadcastreceiver


【解决方案1】:

最终我找到了我想要的干净且现代的解决方案。诀窍是将 BroadcastReceiver 包装在 LiveData 中。通过使用此模式,您可以直接从片段中观察连接 LiveDada 并更新 ui。当然,也有可能(就像提到的 sergiy tikhonov 一样)在基础活动中有一个伴随对象,其 LiveData 从 BroadcastReceiver 更新,您可以从片段中观察它,但我不喜欢这种方法。

所以它是这样工作的:

创建一个包含网络状态的枚举。

enum class NetworkAvailability {
    UNKNOWN,
    CONNECTED,
    DISCONNECTED
}

创建扩展 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)
    }
}

最后我观察了片段中的 LiveData。

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

【讨论】:

  • 干得好。真的帮了我。
【解决方案2】:
  1. 基础/帮助类
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 类
    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. 在数据绑定布局文件中
<?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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-05
    • 1970-01-01
    • 1970-01-01
    • 2013-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多