27

I am struggling in choosing the right way to pass data from broadcastReceiver to ViewModel and from there I pass data to my Repository and update LiveData. I use FCM push notifications and have local broadCastReceiver which uses ActivityLifecycle.

I found that it is bad practice to access ViewModel from BroadcastReceiver, but not sure why?

If I manage lifecycle of broadcastReceiver it should not cause any problems... So what is the best way to pass received data from FCM to my Repository's MediatorLiveData? I use MediatorLiveData, because I add different LiveData sources(API request and FCM).

Would be grateful for advice and correct way of implementing broadCastReceiver.

I have thought about accessing Repository from BroadCastReceiver, like this:

RepositoryMain.getSingletonInstance().setResponse(state);
Edric
  • 24,639
  • 13
  • 81
  • 91
Viktor Vostrikov
  • 1,322
  • 3
  • 19
  • 36

2 Answers2

33

You need to define single source of truth (SSoT). In your case it Repository (if Repository encapsulate db persistence storage, SSoT it is db). Now you need to implement data flow from receiver to view through SSoT (Repository) like in example below:

Receiver implementation

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

Repository

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

View model

public class MyViewModel extends ViewModel {

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

Activity

There is binding of receiver data and 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);
    }
}
Sergei Bubenshchikov
  • 5,275
  • 3
  • 33
  • 60
  • 2
    Really great information! Did that it in similar manner, however managed sources differently and I see that your way is much better. However, after implementing business logic with MediatorLiveData I feel like RxJava might be a better choice for future. It is easier to chain different sources using that tool. How is your experience with RxJava for business logic instead of MediatorLiveData? – Viktor Vostrikov Aug 03 '18 at 17:12
  • @ViktorVostrikov I'm not familiar with RxJava yet, but it written in my to-do list – Sergei Bubenshchikov Aug 05 '18 at 15:14
  • 1
    @XIII-th i have followed this but unable to achieve result, in broadcast receiver after updating the livedata it is not triggering the repository,viewmodel. can you please help me out with this. – YBDevi May 08 '20 at 11:59
  • @YBDevi welcome to my chat room. Let's discuss your case https://chat.stackexchange.com/rooms/info/107763/xiii-th – Sergei Bubenshchikov May 08 '20 at 15:41
  • @XIII-th sorry i was logged out from my work unable to check my SO. When ur free can we discuss about this issue – YBDevi May 11 '20 at 07:47
  • 3
    Great idea to create a public observable data object in the BroadcastReceiver! For production, I would recommend moving the Repository interactions to a ViewModel to handle all data requests between the network and any local storage like Room database. – AdamHurwitz Jun 24 '20 at 01:11
  • 11
    This violates CLEAN Architecture. The Activity shouldn't know anything about Repository, it should only communicate with the ViewModel. The ViewModel can delegate to the Repository. – Christopher Perry Oct 31 '20 at 18:19
  • How exactly we are going to observe the livedata variable here? do we need to create a var of livedata in activity or fragment and set the value from BroadcastReceiver to it, then we will observe or how it will give me realtime update? @XIII-th – Sudhansu Sekhar Aug 24 '21 at 04:14
1

I think you don't need to access the BroadCastReceiver within the viewmodel. Alternatively, use the BroadCastReceiver to move data between activities, if you want to do any logic to the data, send it to the viewModel related to that activity.

In Simple words: Suppose we have the following components:
ActivityOne observes ViewModelOne
ActivityTwo observes ViewModelTwo
BroadCastReceiver [Send actions from ActivityOne]. ActivityTwo Listens to those actions

once ActivityOne receives data from the viewModelOne, it sends the data via the BroadCastReceiver.

ActivityTwo has registered for the BroadCastReceiver, thus it receives those actions, if it dose need to do any logic to the data, it can send it to the ViewModelTwo.

هيثم
  • 791
  • 8
  • 11