9

I'm trying to make an android application which listens to wifi change broadcast and do some action. But the problem is that it is not working when the process is killed. I found this question which says that it won't work without an activity
How to create BroadcastReceiver without Activity/Service?

So that is not an alternative. Hence I created an activity which is empty. I don't want to create a service which keeps running in the background. How can I make my application keep listening even when it is killed. I have registered the broadcast in the manifest.

<activity
        android:name=".MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <receiver android:name="com.background.service.BroadCastReceiver">  
        <intent-filter>
            <action android:name="android.net.wifi.WIFI_STATE_CHANGED"/>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>  
    </receiver>

This is my class

public class BroadCastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
    //do some action
   }
}
Community
  • 1
  • 1
Alex
  • 1,178
  • 3
  • 9
  • 24
  • 1
    Service for what ? Registering in the Manifest is enough. – Don Chakkappan Nov 26 '14 at 10:07
  • 1
    Well, the `onReceive` is not being called when the app is killed – Alex Nov 26 '14 at 10:11
  • @Alex: How do you kill the app? Can you mention the steps? – Mehul Joisar Dec 05 '14 at 11:53
  • Used a pre-installed app for killing all running processes – Alex Dec 06 '14 at 18:33
  • 1
    "Used a pre-installed app for killing all running processes" -- use a more reliable technique, such as terminating your process via DDMS. It may be that your device is doing a force-stop for those processes, which would be epically stupid, but happens with some devices. Beyond that, what are you using to determine if your `BroadCastReceiver` is getting control? Also, have you launched that activity at least once since you installed the app? Without that, manifest-registered receivers will not work (they're in the same "stopped" state that a force-stop does). – CommonsWare Dec 08 '14 at 00:51
  • @CommonsWare I think the device is doing a force stop. But as far as my understand it shouldn't affect the broadcast listener. Right? I'm printing a toggle in `onReceive` to determine if it is getting control and yes I have started the app on installed as tested the toggle. But as soon as I killed it, the toggle stops. – Alex Dec 08 '14 at 04:42
  • "But as far as my understand it shouldn't affect the broadcast listener. Right?" -- wrong. A true "Force Stop" moves apps back to the stopped state, where no manifest-registered receivers will work until an explicit `Intent` is used to invoke one of the app's components (usually, this is the user tapping the launcher icon in the home screen). This is why having "Force Stop" be available more readily than its normal location in Settings is stupid. "I'm printing a toggle" -- I do not know what a "toggle" is in this context (a `ToggleButton`?) and how you print one, sorry. – CommonsWare Dec 08 '14 at 12:22
  • @Alex There may be two issues: 1. some broadcasts are subscribe only (`context.registerReceiver()`) , manifest receivers will not be invoked. 2. As @Dmitry pointed out, try a `WakefulBroadcastReceiver`. – S.D. Dec 08 '14 at 18:26
  • have you got any solution if yes then please share. – Amol Dale Nov 05 '15 at 09:10
  • @CommonsWare You are right, it is certain that going to Settings page and using the 'Force Stop' button will cause manifest-registered receivers to stop working, and that is fine. But on the other hand, I am getting mixed result when I kill the app in 'most recent apps' screen, either by swiping or pressing the X button. 1 or 2 out of 10 times using this method to kill the process causes the receiver to not work anymore, but the rest of the time they do work even though the app is not running (or at least I can't see it in most recent apps screen). Weird. – Bruce Nov 06 '16 at 08:06
  • 1
    I managed to find out why and how to reproduce the behavior I am seeing. This is because I am debugging the app. When the debugger is attached, removing the app from overview screen causes it to move to 'Force Stop' state (confirmed by going to Settings > app and see that 'Force Stop' button is disabled) every single time. Happens both in emulator and real device. Running the app without attaching the debugger have no issues :) Hope this info helps someone! – Bruce Nov 07 '16 at 01:58

6 Answers6

3

Looks like you have it correct defining in the manifest with one exception. The broadcast receiver will trigger an ANR if it doesn't complete in 10 secs. http://developer.android.com/training/articles/perf-anr.html

in your broadcast receiver simply start a service.

public class ReceiverUpload extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
    context.startService(new Intent(context, ServiceUploader.class));
}
}

Then in your service start an asynctask because you don't want to be on the UI thread for example you start the same service from an activity in your app.

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // start asynctask to get off this thread
    new MyBackGround().execute(startId);
    return Service.START_REDELIVER_INTENT;
}

First thing to do in the asynctask is check for wifi

Below is excerpt from a function I call to check network if it returns false the asynctask just finishes if it's true it does network stuff which hey has to be in the background anyways so asynctask makes even more sense.

// check for network connection
        ConnectivityManager connMgr = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connMgr == null) {
            return false;
        }

        // check ok to process
        NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        if (networkInfo == null || !networkInfo.isConnected()) {
            return false;
        }

        boolean isWifi = networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
        if (!isWifi) {

                return false;

        }

        return true;

Note in this example the startId passed to the asynctask is used to cancel OS redelivering the intent.

 @Override
    public void onPostExecute(Integer startId) {
        if (startId != null) {
            stopSelfResult(startId);
        }
    }
danny117
  • 5,581
  • 1
  • 26
  • 35
  • I got a couple up votes on this in the past couple days. I wanted to point out that the main reason you start a service then an async task is so you can put up a cool nifty notification that says "Hey I'm uploading in the background" with a progress bar maybe even a cancel button. Just so the user knows your app is playing fair when it runs in the background. – danny117 Dec 06 '16 at 18:23
2

You already doing right with the Broadcast Receiver and declaring it the Manifest. That's all you need to do. No services running in the background are needed.

Just make sure you install and run the app at least once otherwise the broadcast receives won't be registered

2

The best that worked for me:

AndroidManifest

<receiver android:name="com.AEDesign.communication.WifiReceiver" >
  <intent-filter android:priority="100">
    <action android:name="android.net.wifi.STATE_CHANGE" />
  </intent-filter>
</receiver>

BroadcastReceiver class

public class WifiReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {

  NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
  if(info != null) {
     if(info.isConnected()) {
        // Do your work. 

        // e.g. To check the Network Name or other info:
   WifiManager wifiManager=(WifiManager)context.getSystemService(Context.WIFI_SERVICE);
        WifiInfo wifiInfo = wifiManager.getConnectionInfo();
        String ssid = wifiInfo.getSSID();
     }
  }
 }
 }           

Permissions

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Elango
  • 412
  • 4
  • 24
1

Normally, if app is killed and broadcast comes your app process will be started by system for you. There is absolutely no need to keep something alive or do any additional manipulations (otherwise why would you need BroadcastReceiver if you can just use Service?)

Dmitry Zaytsev
  • 23,650
  • 14
  • 92
  • 146
1

best solution is to make a broadcast receiver , it will work

public class NetworkChangeReceiver extends BroadcastReceiver {

@Override
public void onReceive(final Context context, final Intent intent) {
    final ConnectivityManager connMgr = (ConnectivityManager) context
            .getSystemService(Context.CONNECTIVITY_SERVICE);

    final android.net.NetworkInfo wifi = connMgr
            .getNetworkInfo(ConnectivityManager.TYPE_WIFI);

    final android.net.NetworkInfo mobile = connMgr
            .getNetworkInfo(ConnectivityManager.TYPE_MOBILE);

    if (wifi.isAvailable() || mobile.isAvailable()) {
        // Enjoy coding here 

        Log.d("Netowk Available ", "Flag No 1");
    }
}}

in manifest.xml

 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.broadcastreceiverforinternetconnection"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <receiver android:name=".NetworkChangeReceiver" >
        <intent-filter>
            <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            <action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
        </intent-filter>
    </receiver>
</application>

lalit vasan
  • 450
  • 3
  • 6
0

Updated answer 05/2022

The docs on Broadcasts and Broadcast limitations inform that from Android 7 (Android 8 made it even more stringent)

you cannot use the manifest to declare a receiver for implicit broadcasts (broadcasts that do not target your app specifically), except for a few implicit broadcasts

That means, for most use cases manifest-declared Broadcast won't work anymore and must be replaced with scheduled jobs or services. Be aware, the latter suffers from being killed by the OS for battery optimization.

alexanderdavide
  • 1,487
  • 3
  • 14
  • 22