19

I am checking Internet connectivity in my app using BroadcastReceiver and I show an alert dialog if the connection is lost. It works fine. But my problem is that BroadcastReceiver works even if my app is in backgroung. So dialog pops up when internet connection is lost even if user is in some other app. This is totally ruining my app.

Has anyone got any idea how to restrict Broadcast receiver in the app only?

Here is my BroadcastReceiver :

 public class ConnectivityChangedReceiver extends BroadcastReceiver{

    @Override
    public void onReceive( Context context, Intent intent )
     {
        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService( Context.CONNECTIVITY_SERVICE );
    NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
    if (networkInfo != null && networkInfo.isConnected()) {
    } else {
        try{
            Intent i=new Intent(context, InternetDialogActivity.class);
            i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(i);
        } catch(Exception e){
            e.printStackTrace();
        }
    }
  }
}

And the activity is:

 public class InternetDialogActivity extends Activity implements OnClickListener{
   @Override
    public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.internet_dialog_box);
      getWindow().setLayout(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
      Button retryButton = (Button) findViewById(R.id.retryInternetButton);
      retryButton.setOnClickListener(this);
    }

   public boolean checkConnectivity(){
       ConnectivityManager connectivityManager   = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
           NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();

       if (networkInfo!=null && networkInfo.isConnected()) {
           finish();
           return true;
       } else {
           Intent intent = getIntent();
           finish();
           startActivity(intent);
           return false; 
      }
   }

   @Override
   public void onClick(View view) {
      switch(view.getId()){
         case R.id.retryInternetButton:
             try{
                 checkConnectivity();
             } catch(Exception e){
                 e.printStackTrace();
             }
             break;
       }
   }
   }

Here is how I declared receiver and activity in manifest:

  <receiver android:name="com.lisnx.service.ConnectivityChangedReceiver"
        android:label="NetworkConnection">
        <intent-filter>
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
        </intent-filter>
  </receiver>

  <activity android:name="com.lisnx.activity.InternetDialogActivity"
        android:configChanges="orientation|keyboardHidden"
        android:theme="@android:style/Theme.Dialog" />

I have read that we can restrict BroadcastReceiver to work within the app by not declaring it in the manifest. But I don't know how will receiver work then? Please help me. I am stuck on it badly. Thanx in advance.

3
  • before you called intent in your broadcast receiver, you can check that if my app is running foreground or not. if is true then only call your intent, here is the link you can get the foreground apps. stackoverflow.com/questions/8489993/… Commented Sep 5, 2012 at 5:42
  • @HirenDabhi : I saw it and its a good idea but then I saw this: stackoverflow.com/questions/3667022/… ..... And it says that your solution is not a robust one. :( Any other ideas? Commented Sep 5, 2012 at 5:55
  • 1
    @DavidWasser's answer is the correct way to do this. Do not try to hack a manifest BroadcastReceiver, it is much better to dynamically register & unregister a receiver in your code. Manifest receivers are for a different use case. Commented Apr 21, 2014 at 7:12

9 Answers 9

37

A BroadcastReceiver works when the app is in the background because the event that the receiver picks up are sent globally, and each app is registered to listen in on these, regardless of whether or not it is running.

To deal with this, in your BroadcastReceiver's onReceive code, check if your app is in the foreground.

There is one--and only one that I know of--consistently effective method to do this. You need to keep track of your pause/resume actions for your application. Ensure that you check this in every activity.

There is some sample code in this answer (solution #1). In your case, you would want to check MyApplication.isActivityVisible() == true as a validation before doing anything from your BroadcastReceiver.

1
  • 10
    This is not an optimal solution. The optimal solution is to call the BroadcastReceiver in the main activity (or in the activity that you want) instead to declare it in the android manifest file. Creating the broadcast receiver within the application, and not the manifest, allows your application to only handle detached events while it is running. This way, detached events are only sent to the application that is currently running and not broadcast to all applications.
    – SerSánGal
    Commented Oct 8, 2015 at 11:12
18

Have you tried to remove the Intent filter from the manifest and register/unregister it in activity? So you can try to register Intent filter in onStart() and unregister it on onStop() methods. The code goes somethink like this:

static final String ACTION = "android.net.conn.CONNECTIVITY_CHANGE";


IntentFilter filter = new IntentFilter(ACTION);
this.registerReceiver(ConnectivityChangedReceiver, filter);


unregisterReceiver(ConnectivityChangedReceiver);

You should also learn about Activity Lifecycle, if it's not familiar yet.

6
  • 1
    So do I have to do it in every Activity? Commented Sep 5, 2012 at 5:59
  • 3
    +1 I think this is the best approach to solve OP's problem (although I would do it in onPause() and onResume() instead of onStop() and onStart()). Yes, it has do be done in every activity, but you can easily do that by creating your own BaseActivity that extends Activity and overrides onPause() and onResume() and registers/unregisters your receiver. Just have all your activities extend BaseActivity() and have them call through to super.onResume() and super.onPause() as usual. Commented Sep 5, 2012 at 6:15
  • Well, like you can see on Activity lifecycle: other activity comes into the foreground, activity goes to the onPause() method, so intent filter is still registered. This is handy if you have multiple activities on project, but it might be wise to check that is the filter already registered, on every activity.
    – soreal
    Commented Sep 5, 2012 at 6:16
  • @DavidWasser : Can you please elaborate it as an answer. Looks the most feasible approach. Commented Sep 5, 2012 at 6:23
  • 1
    Yes, i might reg/unreg filter in onResume() and onPause() in this case. Like @DavidWasser says, it would be good to create BaseActivity to handle reg/unreg for each activities in app. Also might be good to create some boolean for checking is the filter registered yet.
    – soreal
    Commented Sep 5, 2012 at 6:40
14

You should register/unregister your BroadcastReceiver in onPause() and onResume() of each activity. Then you know that the receiver is only "listening" when your app is in the foreground. You can easily do that by creating your own BaseActivity that extends Activity and overrides onPause() and onResume() and registers/unregisters your receiver. Just have all your activities extend BaseActivity and have them call through to super.onResume() and super.onPause() as usual. Here's example code:

public class BaseActivity extends Activity {
    // Create an IntentFilter to listen for connectivity change events
    static IntentFilter filter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
    // Create an instance of our BroadcastReceiver
    static ConnectivityChangedReceiver receiver = new ConnectivityChangedReceiver();

    @Override
    protected void onPause() {
        super.onPause();
        // Stop listening for connectivity change events
        unregisterReceiver(receiver);
    }

    @Override
    protected void onResume() {
        super.onResume();
        // Listen for connectivity change events
        registerReceiver(receiver, filter);
    }
}

All your activities should extend BaseActivity and if they need to do anything special in onResume() or onPause() just make sure to call through to super.onXXXXX() like this:

public MyActivity extends BaseActivity {
    @Override
    protected void onResume() {
        super.onResume();
        // Here you do whatever you need to do in onResume() of your activity
        ...
    }

    @Override
    protected void onPause() {
        super.onPause();
        // Here you do whatever you need to do in onPause() of your activity
        ...
    }
}

I didn't run the code through a compiler so I apologize if there's a typo or I missed something.

4
  • It completely crashes the app if internet connection is lost. It goes back to all the activities one by one , through which user had navigated and shows the alert dialog sometimes and then app is finally closed itself. I think we are missing here something. Commented Sep 5, 2012 at 14:53
  • there is no Exception in the logcat. I think its a problem of onPause and onResume automatically being called. Commented Sep 6, 2012 at 4:23
  • No, you don't. As long as you never start an instance of BaseActivity you don't need it in the manifest. Post some code and logcat errors and we can help you more. Commented Sep 6, 2012 at 8:00
  • That is how you check broadcasts in a screen specific manner. Only the screen displaying will receive broadcasts in this way. When you register broadcast using manifest then they are received independent of the app which means that your app doesn't have to be in foreground to actually receive broadcasts. Commented May 8, 2014 at 11:08
4

I think you will have to make sure that you are not using the receiver when app is in background. For that you will have to use every activities onPause() and onResume() methods.

4

As far as I know, if Broadcast receiver is registered inside manifest.xml then broadcast receiver exists as long as application exists. Also, Dynamically registered receivers (that means, Register your BroadcastReceiver programmatically) are called on the UI thread. This means that your receivers blocks any UI handling and thus the onReceive() method should be as fast as possible.

However, I will try to discuss information about Broadcast Receiver. But, first should know some information. Firstly, Broadcast receiver is a standalone application component which means it will continue running even when other application component are not running. That's why we unregister broadcast receiver in onPause on the activity. Also, Developer should register this in Activity.onResume() implementation.

Secondly, Developer should not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back in the history stack. I have put that information from BroadcastReceiver documentation.

Another point is that a BroadcastReceiver object is only valid for the duration of the call to onReceive(). As soon as the onReceive() method is finished, your BroadcastReceiver terminates.

Now, how to register your receiver programmatically:

public abstract Intent registerReceiver (BroadcastReceiver receiver, IntentFilter filter)

Here, BroadcastReceiver- receiver will be call when any broadcast intent match with filter.
And IntentFilter- Intent specifies which event your receiver should listen to.

Register:

YourConnectionListener receiver;
this.reciever = new YourConnectionListener();
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
registerReceiver(this.reciever, filter);

Sent your Broadcast Info:

Intent intent = new Intent();
intent.putExtra("Message", "Your connectivity info has Changed!!"); 
this.sendBroadcast(intent);

Receiver:

Now, need to receive the Broadcast. Android calls the onReceive() method on all registered broadcast receivers whenever the event occurs. Say you want to be notified whenever the connection is changed.

public class YourConnectionListener extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent){
         // your Code
    }
}   

onReceive() has two arguments:

  • context: The Context object you can use to access additional information or to start services or activities.
  • intent: Intent used to register your receiver. This object contains additional information that you can use in your implementation.
    Additionally, Developer should avoid any long-lasting tasks in your BroadcastReceiver. So, In statically and dynamically registered receivers, Developer should do minor tasks in the receiver itself.For any longer tasks you should start a service from within your receiver.
2
  • +1 from me. I think I'll have to do it in using onPause and onResume eventually. Commented Sep 5, 2012 at 7:39
  • when you say: As soon as the onReceive() method is finished, your BroadcastReceiver terminates. I understand that upon another broadcast(of the action I'm listening to), the BroadcastReceiver is executed again, right? Commented Oct 8, 2013 at 15:51
3

To make a Broadcast Receiver that fires only when you app is running follow the below code.

1. Create your Broadcast Receiver like this:


import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class InternetStatusNotifier extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        //Recieve notification here

    }

}

2. Make an activity or fragment where you want the Broadcast Receiver to work like this:


import android.app.Activity;
import android.content.IntentFilter;
import android.os.Bundle;

public class MainActivity extends Activity {
    private InternetStatusNotifier mInternetStatusNotifier;

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

    @Override
    protected void onResume() {
        registerReceiver(mInternetStatusNotifier, new IntentFilter(
                "android.net.conn.CONNECTIVITY_CHANGE"));
        super.onResume();
    }

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

Note: That is how you use broadcasts receiver in a screen specific manner. Only the screen displaying will receive broadcasts in this way. When you register broadcast using manifest file then they are even received when app is closed

1

That is the way broadcast receivers work in Android. If you register for a broadcast in the manifest and your app is not running, Android will start a new process to handle the broadcast. It is generally a bad idea to directly show a UI from a broadcast receiver, because this may interrupt other apps. I'm also not convinced that a universal 'connection lost' dialog is a good idea either. This should probably be handled by each activity that uses network access.

As for the original question, you need to disable your receiver when your activity goes in the background (onPause(), etc.) and enable it when you come to the foreground (onResume(), etc). Put enabled=false in your manifest and then use something like this in your code to toggle it as necessary:

   public static void toggle(Context context, boolean enable) {
        int flag = enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
        ComponentName receiver = new ComponentName(context,
                ConnectivityMonitor.class);

        context.getPackageManager().setComponentEnabledSetting(receiver, flag,
                PackageManager.DONT_KILL_APP);
   }
6
  • looks good. But I've read somewhere that if we don't define receiver in manifest and register/unregister it dynamically, then it won't happen. Do you have any idea how to do that? Commented Sep 5, 2012 at 5:43
  • 1
    'Read somewhere' is overly vague. The behaviour is different for different receivers: some need to be registered in code, some need to be in the manifest. This is the API but you need to check details about each specific broadcast: developer.android.com/reference/android/content/…, android.content.IntentFilter) Commented Sep 5, 2012 at 5:53
  • sorry for the inconvenience. I read it here: stackoverflow.com/questions/5725417/… Commented Sep 5, 2012 at 5:57
  • That answer is not entirely correct: you can disable/enable receivers registered in the manifest. The code above is from a real app, and it does work. Commented Sep 5, 2012 at 6:15
  • But where and how can I use this method? Commented Sep 5, 2012 at 14:50
0

A simple way of finding whether the app is in foreground or not

if((mContext.getPackageName().equalsIgnoreCase(
                ((ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE))
                        .getRunningTasks(1).get(0).topActivity.getPackageName())))
{
//app is in foreground;
}
0
-1

I better suggest you to check the internet setting from the application when someone opens it, here is the piece of code how i do it.

public static boolean isNetworkConnected(Context ctx) {  
        ConnectivityManager cm = (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);  
        NetworkInfo ni = cm.getActiveNetworkInfo();  
        if (ni == null) {  
            return false; // There are no active networks.  
        } else  
            return true;  
    }

Not the answer you're looking for? Browse other questions tagged or ask your own question.