19

I have a BroadcastReceiver implementation that receives network connection events. its declared in the AndroidManifest.xml and is called by Android automatically when network events occur.

BroadcastReceiver:

public class ConnectivityChangeReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.v(TAG, "action: " + intent.getAction());
        Log.v(TAG, "component: " + intent.getComponent());
    }
}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.test">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...
        <receiver
            android:name=".ConnectivityChangeReceiver"
            android:enabled="true">
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

I'd like to use Google's MVP sample architecture described here for my app:

https://github.com/googlesamples/android-architecture/tree/todo-mvp/

Using the above architecture, just wondering:

  1. Where should my BroadcastReceiver be placed?

  2. If my BroadcastReceiver needs to write to the database, whats the best way of doing this?

  3. If my BroadcastReceiver needs to update the UI, whats the best way of doing this?

Rory
  • 4,030
  • 4
  • 17
  • 21
  • How about using EventBus ? https://github.com/greenrobot/EventBus – Chintan Soni Sep 23 '16 at 11:03
  • @Rory How about an intent service, which needs to be explicitly started and stopped during your application flow ? – Sreehari Oct 03 '16 at 08:58
  • @ChintanSoni what about big mud of mess?! ;)) He is trying to make some architecture not to solve it quick and dirty by adding "component" that will talk to everyone and listen to everyone without any obligation or contract. – Ewoks Mar 23 '17 at 06:11
  • 1
    @Ewoks Hope changing perception helps you ;) – Chintan Soni Mar 23 '17 at 06:15
  • Just wondered what part of MVP (or some other architecture) that EventBus would belong to...Thinking about it changed my perspective – Ewoks Mar 23 '17 at 06:27

5 Answers5

12
  1. Personally, I believe that events from the BroadcastReceiver should be delivered to the presenter.
  2. Based on statement 1, presenter holds reference to the Interactor/Contract/Use case that should handle db operations.

    BroadcastReceiver --event--> Presenter --> Interactor ---> Repository

  3. Based on statement 1, again the presenter should consume the event and make call to the View.

    BroadcastReceiver --event--> Presenter --> (maybe do some stuff, business logic) ---> View

Here is what I have, a minimal example snippet that sums up what I said:

  private class NetworkBroadcastReceiver23 extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //... redacted code.../
            boolean connected = activeNetworkInfo != null && activeNetworkInfo.isConnected();
            mPresenter.onConnectionChanged(activeNetworkInfo,connected);
        }
    }

Place the receiver in the activity, because from there you stream events to the presenter. This will ease testing the presenter for connection change. It's hard to achieve separation of concern for platform events, I wanted to keep my layers free from android sdk components and classes. Another way, that was pointed by Alex Shutov, is mixing MVP and Observer pattern if you go with considering BroadcastReceiver as external entity, rather than event source.

Yes, I agree that you can improve the method by getting rid of the NetworkInfo param.

Nikola Despotoski
  • 49,966
  • 15
  • 119
  • 148
3

In MVP design pattern Model has all entities for connection with outer world (for example, Repository for fetching data and persisting it locally). Broadcast receiver is a input for external events, which, eventually, will modify model. Good comparison is a 'Input port' in Hexagonal architecture.

Presenter defines the way of displaying data from model, but all business logic, including reaction to another system or user events is supposed to be inside model.

View and Presenter can be changed dynamically depending on mode program running in, say, you want to use another version of UI, or more simplistic UI behaviour, but all logic must remain the same, including reaction to external events. That is why BroadcastReceiver should be placed inside model.

You should NEVER place Broadcast receiver into Activity, because Activity is a system container for (View), at least if you follow MVP pattern. In case your project is quite complex consider abstracting from BroadcastReceiver by some 'ExternalInput' interface, which can be easily mocked during testing and use it inside Model.

Nikola Despotoski
  • 49,966
  • 15
  • 119
  • 148
Alex Shutov
  • 3,217
  • 2
  • 13
  • 11
0

If you are packaging by feature, you can just create receiver package inside feature package and put BroadcastReceiver class in it. If you don't have subpackages, just put BroadcastReceiver class in your feature package.

pavle
  • 909
  • 14
  • 38
-3

Your broadcast should be View. Then, it call Presenter method, that changes state of some NetworkStateService(which is Model level). When state of NetworkStateService changed, it notify Presenters, that network is available and they can make requests. And these Presenters should update UI. All of these Presenters should be as listeners in NetworkStateService.

For long operations, such as work with db or network, you should start Service. The reason for this is that broadcast will be killed after 10 seconds from receive. You should put Presenter into this Service and work with Model from this Presenter.

senneco
  • 1,786
  • 2
  • 12
  • 15
  • *Your broadcast should be View* - I don't know in what universe that is true, but not in this one – Tim Dec 15 '17 at 15:26
  • @TimCastelijns could you explain yourr universe?) In my universe all inputs may be put in View layer, because it's input. Also you can put it in domain layer. It's depends on input type. And if user turn off network it's user inpu %) – senneco Dec 16 '17 at 19:51
-3

If you need to do database operations/Update UI then you should place BroadcastReceiver in corresponding activity.

Here is the sample code.

public class YourActivity extends AppCompatActivity  {

    @Override
    protected void onResume() {
        super.onResume();
        registerReceiver(mConnectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
      // public static final String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
    }

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mConnectivityReceiver);
    }


    public BroadcastReceiver mConnectivityReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            checkIntent(intent);
        }

        private void checkIntent(Intent intent) {
            Bundle bundle = intent.getExtras();
             if (bundle != null) {
                 NetworkInfo networkInfo = (NetworkInfo) bundle.get("networkInfo");
                    doYourStuff(networkInfo.isConnected());
            }
        }
    };

    private void doYourStuff(boolean isNetworkConnected) {
        //Update your UI here
        //Do database Operations here
    }
}

Do your stuff in doYourStuff() based on network connectivity.

Through this approach you don't need to register your BroadcastReceiver in Menifest.xml file.

waqas ali
  • 1,238
  • 1
  • 11
  • 17
  • I believe registering the BroadcastReceiver in code vs an activity means that my app won't receive broadcasts when it is not running, which I want. – Rory Sep 27 '16 at 11:03
  • Yes it app won't receive broadcast because calling unregisterReceiver will: 1: Unregister a previously registered BroadcastReceiver. 2: filter that have been registered for this BroadcastReceiver will be removed. – – waqas ali Sep 27 '16 at 11:35
  • If this helped you mark this answer best for others. – waqas ali Sep 27 '16 at 11:36
  • Activity is a pure UI system container in Android, that is why you should NEVER place Broadcast receiver in it if it modifies model state. – Alex Shutov Sep 28 '16 at 13:20
  • Its depends on requirement.If your app wants to use BroadcastReceiver while app is in foreground then you should go for this method as here required. http://stackoverflow.com/a/7636675/5124050 – waqas ali Sep 28 '16 at 14:34