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.

4

9 回答 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.

于 2012-09-05T05:34:59.540 回答
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.

于 2012-09-05T05:58:19.897 回答
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.

于 2012-09-05T08:32:32.833 回答
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.
于 2012-09-05T07:20:36.140 回答
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.

于 2012-10-25T13:05:11.727 回答
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

于 2014-05-08T11:30:56.980 回答
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);
   }
于 2012-09-05T05:36:56.603 回答
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;
}
于 2014-01-02T16:13:31.747 回答
-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;  
    }
于 2015-03-14T14:57:23.947 回答