在Viewmodel中访问BroadCastReceiver

27

我在选择从广播接收器传递数据到ViewModel的正确方式以及从那里传递数据到我的Repository并更新LiveData方面遇到了困难。我使用FCM推送通知,并且有一个使用ActivityLifecycle的本地广播接收器。

我发现从BroadcastReceiver访问ViewModel是不良实践,但不确定为什么?

如果我管理广播接收器的生命周期,它不应该引起任何问题...那么将接收到的数据从FCM传递给我的Repository的MediatorLiveData的最佳方法是什么? 我使用MediatorLiveData,因为我添加了不同的LiveData来源(API请求和FCM)。

感谢建议和正确实现广播接收器的方式。

我已经考虑从BroadCastReceiver访问Repository,如下所示:

RepositoryMain.getSingletonInstance().setResponse(state);
2个回答

33

您需要定义单一真相来源 (SSoT)。在您的情况下,它是存储库 (如果存储库封装了数据库持久性存储,则SSoT就是数据库)。现在,您需要通过SSoT (存储库) 实现从接收器到视图的数据流,如下面的示例:

接收器实现

public class FcmReceiver extends BroadcastReceiver {

    private final MutableLiveData<MyData> mData = new MutableLiveData<>();

    public LiveData<MyData> getData() {
        return mData;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        // entry point of data
        mData.setValue(new MyData());
    }
}

仓库

public class Repository {

    private static final Repository INSTANCE = new Repository();

    private final MediatorLiveData<MyData> mData = new MediatorLiveData<>();

    private Repository() {}

    public static Repository instance() {
        return INSTANCE;
    }

    public LiveData<MyData> getData() {
        return mData;
    }

    public void addDataSource(LiveData<MyData> data) {
        mData.addSource(data, mData::setValue);
    }

    public void removeDataSource(LiveData<MyData> data) {
        mData.removeSource(data);
    }
}

视图模型

public class MyViewModel extends ViewModel {

    public LiveData<MyData> getData() {
        // for simplicity return data directly to view
        return Repository.instance().getData();
    }
}

活动

接收器数据和Repository的绑定。

public class MainActivity extends AppCompatActivity {

    private FcmReceiver mReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mReceiver = new FcmReceiver();
    }

    @Override
    protected void onStart() {
        super.onStart();
        // add data source
        Repository.instance().addDataSource(mReceiver.getData());
        registerReceiver(mReceiver, IntentFilter.create("action", "type"));
    }

    @Override
    protected void onStop() {
        super.onStop();
        // don't forget to remove receiver data source
        Repository.instance().removeDataSource(mReceiver.getData());
        unregisterReceiver(mReceiver);
    }
}

2
真是太棒的信息!我以类似的方式做了,但是管理资源的方式不同,我发现你的方法更好。然而,在使用MediatorLiveData实现业务逻辑后,我觉得RxJava可能是未来更好的选择。使用这个工具可以更容易地链接不同的资源。相比MediatorLiveData,你在使用RxJava处理业务逻辑方面有什么经验吗? - Viktor Vostrikov
1
@XIII-th 我已经按照这个方法尝试过了,但未能取得结果。在广播接收器中更新LiveData后,它并没有触发Repository和ViewModel。你能帮我解决一下吗? - YBDevi
@YBDevi 欢迎来到我的聊天室。让我们讨论一下你的案例 https://chat.stackexchange.com/rooms/info/107763/xiii-th - Sergei Bubenshchikov
3
在 BroadcastReceiver 中创建公共可观测数据对象的想法很棒!对于生产环境,我建议将 Repository 交互移到 ViewModel 中,以处理网络和本地存储(如 Room 数据库)之间的所有数据请求。 - AdamHurwitz
11
这违反了“干净架构”原则。Activity 不应该了解 Repository 的任何信息,它只能与 ViewModel 进行通信。ViewModel 可以委托 Repository。 - Christopher Perry
显示剩余3条评论

1
我认为您不需要在ViewModel中访问BroadCastReceiver。相反,如果您想对数据进行任何逻辑处理,请使用BroadCastReceiver在活动之间传递数据,并将其发送到与该活动相关的ViewModel。
简单来说: 假设我们有以下组件: ActivityOne观察ViewModelOne ActivityTwo观察ViewModelTwo BroadCastReceiver [从ActivityOne发送操作]。 ActivityTwo侦听这些操作
一旦ActivityOne从viewModelOne接收到数据,它会通过BroadCastReceiver发送数据。
ActivityTwo已注册BroadCastReceiver,因此它将接收到这些操作。如果需要对数据执行任何逻辑操作,则可以将其发送到ViewModelTwo。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接