3

I've written an android app which checks the network status by using a BroadcastReceiver inherited class:

public class NetworkChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(final Context context, final Intent intent) {
        Log.d("mylog", "NetworkChangeReceiver Hit");
    }
}

which is registered in the manifest file like this:

<receiver
    android:name="foo.NetworkChangeReceiver"
    android:label="NetworkChangeReceiver" >
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
        <action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
    </intent-filter>
</receiver>

I needed to handle the internet connection whenever it connects or disconnects. Actually, it works perfectly in normal situations.

But the problem is that when the application is closed manually (by minimizing the app and then closing it by swiping out the app icon in the Recents button menu), it still receives the network status changes. This sometimes causes some exceptions.

Even I have included all the code in receiver function in try/catch block, but still sometimes a toast message containing an error message is shown. This sometimes happen even after some days after the closure of the app.

Please note that the code in the receiver function is more complicated than the code that is shown here and has some access to internal classes and variables.

mehrdad seyrafi
  • 3,084
  • 2
  • 19
  • 16

3 Answers3

2

Your app will still receive events, even if it isn't running. Before you do anything in onReceive(), you can check if the activity is running by:

Option 1: Use a static variable in your activity to check it's state for access from the receiver :

public class YourActivity extends Activity {


 public static boolean isRunning = false;

 @Overrride
 public void onCreate(Bundle savedInstanceState){
 isRunning = true;
  ....
 }
   //We need receiver to work when app is minimized
   /*
      @Override
      public void onStart() {
         super.onStart();
         isRunning = true;
      } 


      @Override
      public void onStop() {
         super.onStop();
         isRunning = false;
      }
*/

}

And in the receiver:

public class NetworkChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(final Context context, final Intent intent) {
        Log.d("mylog", "NetworkChangeReceiver Hit");
            if(!YourActivity.isRunning)
              return;
    }
}

Option 2 : Using the ActivityManager

public class NetworkChangeReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {

    if (isAppForground(context)) 
         return;

}

public boolean isAppForground(Context mContext) {

    ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
    List<RunningTaskInfo> tasks = am.getRunningTasks(1);
    if (!tasks.isEmpty()) {
        ComponentName topActivity = tasks.get(0).topActivity;
        if (!topActivity.getPackageName().equals(mContext.getPackageName())) {
            return false;
        }
    }

    return true;
}

}

You'll need the permission:

<uses-permission android:name="android.permission.GET_TASKS" />
Nana Ghartey
  • 7,901
  • 1
  • 24
  • 26
  • Dear Nana, neither of your options work for me. For the first option, I need the receiver to work when it is minimized. But onStop is called when the app is minimized. So checking isRunning flag prevents the reciever from working. My solution could be using onDestroy instead of onStop, but the problem is that onDestroy is not called in all situations, specially when the app is closed by swiping out the app icon from the Recents list. The second option doesn't fit to my problem because of similar reason. – mehrdad seyrafi Jan 17 '15 at 21:14
  • You can still use option 1. Just set the isRunning flag in onCreate() and comment out onStart() and onStop(). isRunning will reset to false when the app is closed via the recent list. I've updated my answer – Nana Ghartey Jan 18 '15 at 14:58
  • I dont know who down voted this answer.. it is exactly what I needed. I used the option 1 and it worked like a charm. Thanks for the answer. Up-voted! – wendelbsilva Sep 29 '15 at 15:47
1

If you define receivers in your manifest, the app will receive events, even if it is not started.

http://developer.android.com/guide/topics/manifest/receiver-element.html

Broadcast receivers enable applications to receive intents that are broadcast by the system or by other applications, even when other components of the application are not running.

To fix this, just don't define the Receiver in the manifest, but do it programatically in onStart and unregister it again in onStop. The problem with this solution is, that you won't get messages if your app is in the background.

private BroadcastReceiver receiver;

@Overrride
public void onStart(){

   super.onStart();

   IntentFilter filter = new IntentFilter();
   filter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
   filter.addAction("android.net.wifi.WIFI_STATE_CHANGED");

   receiver = new NetworkChangeReceiver();
   registerReceiver(receiver, filter);
}

@Override
protected void onStop() {
   super.onStop();
   //don't forget to unregister the receiver again
   unregisterReceiver(receiver);
}

EDIT: onCreate and onDestroy won't work, as onDestroy will not be called in every instance the app is closed (e.g. if it is closed with the task manager)

tknell
  • 9,007
  • 3
  • 24
  • 28
  • Dear my friend, Your solution has a bug and it is that onDestroy will not be called in all situations. For example when the app is closed by swiping out the app icon from the Recents list. In this case the receiver will not be unregistered – mehrdad seyrafi Jan 17 '15 at 21:29
  • Yes, you're right, I'll update my answer about that. – tknell Jan 19 '15 at 08:12
  • This is definitely the most elegant solution (and more correct) – mdelolmo Jul 18 '15 at 15:25
0

Solution Found:

I found a perfect solution to my problem. Thanks to the correct answer in Android service crashes after app is swiped out of the recent apps list, I found out that when an app is closed via Recents list, the whole process will be created again and all the static variables will be freshed to their default values, and the onCreate and all other methods will not be called. So the solution is something like:

public static boolean appStarted = false;
protected void onCreate(Bundle savedInstanceState) {
    appStarted = true;
    ...
}


public class NetworkChangeReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(final Context context, final Intent intent) {
        if (!MyActivity.appStarted)
            return;
        ...
    }
}

The key is to just keep track of when the app starts, and not when the app is closed (because the closing event of app is not dependable and in some situations doesn't work properly)

Community
  • 1
  • 1
mehrdad seyrafi
  • 3,084
  • 2
  • 19
  • 16