从 MvvM 架构和 Kotlin 中的广播接收器更新片段 ui
Update fragment ui from broadcast receiver in MvvM architecture and Kotlin
作为 kotlin 和 mvvm 架构的新手,我浪费了过去两天的时间来寻找这个问题的答案,但没有成功。所以我正在寻找一个干净的解决方案来更改片段 ui (在我的例子中它只是一个文本视图)当设备在互联网上 connected/disconnected 时。我正在使用 mvvm 架构和 android 架构组件(viewmodels、livedata、数据绑定...)。我有一个带有底部导航和多个片段的基础 activity。
我正在尝试从自定义广播接收器获取连接事件,我想以某种方式在视图模型或我的片段中传递该事件。
我的第一个想法是使用界面。此接口由广播接收器的 onreceive 触发,并在我的片段或视图模型中实现,因此当互联网事件发生时,文本视图会在我的片段中更新。但是我不确定如何使用广播接收器中的接口,或者我不知道这是否可行或一个好的做法。
到目前为止我所做的是创建广播接收器
class ConnectionStateObserver: BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
//somehow use the interface in here
}
}
register/unregister它在我的基地activity
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 年的旧解决方案也将是一件好事。提前谢谢你。
最终我找到了一个干净而现代的解决方案,就像我想要的那样。诀窍是将 BroadcastReceiver 包装在 LiveData 中。通过使用此模式,您可以直接从片段中观察连接 LiveDada 并更新 ui。当然,也有可能(如提到的 sergiy tikhonov)在基础 activity 中有一个伴随对象,其中有一个从 BroadcastReceiver 更新的 LiveData,你可以从片段中观察它,但我不喜欢这种方法。
所以它是这样工作的:
创建一个包含网络状态的枚举。
enum class NetworkAvailability {
UNKNOWN,
CONNECTED,
DISCONNECTED
}
创建扩展 LiveData 的 class。我正在使用单例模式,因为我不想要多个实例。我创建了一个 BroadcastReceiver 实例,并在 LiveData 的 onActive 和 onInactive 方法中 register/unregister 它。
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
}
})
- base/helper 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));
}
};
}
- 视图模型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());
}
- 在数据绑定布局文件中
<?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>
作为 kotlin 和 mvvm 架构的新手,我浪费了过去两天的时间来寻找这个问题的答案,但没有成功。所以我正在寻找一个干净的解决方案来更改片段 ui (在我的例子中它只是一个文本视图)当设备在互联网上 connected/disconnected 时。我正在使用 mvvm 架构和 android 架构组件(viewmodels、livedata、数据绑定...)。我有一个带有底部导航和多个片段的基础 activity。
我正在尝试从自定义广播接收器获取连接事件,我想以某种方式在视图模型或我的片段中传递该事件。
我的第一个想法是使用界面。此接口由广播接收器的 onreceive 触发,并在我的片段或视图模型中实现,因此当互联网事件发生时,文本视图会在我的片段中更新。但是我不确定如何使用广播接收器中的接口,或者我不知道这是否可行或一个好的做法。
到目前为止我所做的是创建广播接收器
class ConnectionStateObserver: BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
//somehow use the interface in here
}
}
register/unregister它在我的基地activity
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 年的旧解决方案也将是一件好事。提前谢谢你。
最终我找到了一个干净而现代的解决方案,就像我想要的那样。诀窍是将 BroadcastReceiver 包装在 LiveData 中。通过使用此模式,您可以直接从片段中观察连接 LiveDada 并更新 ui。当然,也有可能(如提到的 sergiy tikhonov)在基础 activity 中有一个伴随对象,其中有一个从 BroadcastReceiver 更新的 LiveData,你可以从片段中观察它,但我不喜欢这种方法。
所以它是这样工作的:
创建一个包含网络状态的枚举。
enum class NetworkAvailability {
UNKNOWN,
CONNECTED,
DISCONNECTED
}
创建扩展 LiveData 的 class。我正在使用单例模式,因为我不想要多个实例。我创建了一个 BroadcastReceiver 实例,并在 LiveData 的 onActive 和 onInactive 方法中 register/unregister 它。
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
}
})
- base/helper 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));
}
};
}
- 视图模型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());
}
- 在数据绑定布局文件中
<?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>