32

I need to keep broadcast receiver running all the time after app has been started.

Here is the code that registers this receiver in the application

    IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
    filter.addAction(Intent.ACTION_SCREEN_OFF);
    BroadcastReceiver mReceiver = new ScreenEventsReceiver();
    registerReceiver(mReceiver, filter);

And code for receiver

public class ScreenEventsReceiver extends BroadcastReceiver {
     public static boolean wasScreenOn = true;

     @Override
     public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
            wasScreenOn = false;
            Log.d("ScreenEventReceiver", "ON");
        } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
            wasScreenOn = true;
            Log.d("ScreenEventReceiver", "ON");
        }
     }
}
thechaoticpanda
  • 396
  • 2
  • 18
Sergey Pekar
  • 8,555
  • 7
  • 47
  • 54
  • 1
    You might want to create a [service](https://developer.android.com/reference/android/app/Service.html) – j.holetzeck May 29 '13 at 21:46
  • But what about receiving events? – Sergey Pekar May 29 '13 at 21:51
  • 1
    Do you really need to monitor screen on/off events? The reason that they force you to register a receiver not in the manifest is that they don't want people to always be monitoring these events and slowing down the device. – natez0r May 29 '13 at 22:12

7 Answers7

30

You can use a service

In main app start/stop the service

Intent service = new Intent(context, MyService.class);
context.startService(service);
...
Intent service = new Intent(context, MyService.class);
context.stopService(service);

service

public class MyService extends Service
{
 private static BroadcastReceiver m_ScreenOffReceiver;

 @Override
 public IBinder onBind(Intent arg0)
 {
  return null;
 }

 @Override
 public void onCreate()
 {
  registerScreenOffReceiver();
 }

 @Override
 public void onDestroy()
 {
  unregisterReceiver(m_ScreenOffReceiver);
  m_ScreenOffReceiver = null;
 }

 private void registerScreenOffReceiver()
 {
  m_ScreenOffReceiver = new BroadcastReceiver()
  {
   @Override
   public void onReceive(Context context, Intent intent)
   {
     Log.d(TAG, "ACTION_SCREEN_OFF");
     // do something, e.g. send Intent to main app
   }
  };
  IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
  registerReceiver(m_ScreenOffReceiver, filter);
 }
}
j.holetzeck
  • 4,048
  • 2
  • 21
  • 29
17

Accepted answer is not an actual answer i think. I will explain what the issue. I think you are testing your app in the Huawie, Oppo, Vivo, Xiomi,asus....... or some devices. With that devices if we close the application they will also close our broadcast receivers. So thats the problem.(To check that use a with pixel nexus emulator). I will explain how to resolve this.``

  • we would add our app to the protected app list. OS only allow to them to continue broadcast receiver activities.(Copy this array declaration to your code)

    private static final Intent[] POWERMANAGER_INTENTS = {
        new Intent().setComponent(new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity")),
        new Intent().setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity")),
        new Intent().setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity")),
        new Intent().setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity")),
        new Intent().setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity")),
        new Intent().setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.startupapp.StartupAppListActivity")),
        new Intent().setComponent(new ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity")),
        new Intent().setComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity")),
        new Intent().setComponent(new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager")),
        new Intent().setComponent(new ComponentName("com.vivo.permissionmanager", "com.vivo.permissionmanager.activity.BgStartUpManagerActivity")),
        new Intent().setComponent(new ComponentName("com.samsung.android.lool", "com.samsung.android.sm.ui.battery.BatteryActivity")),
        new Intent().setComponent(new ComponentName("com.htc.pitroad", "com.htc.pitroad.landingpage.activity.LandingPageActivity")),
        new Intent().setComponent(new ComponentName("com.asus.mobilemanager", "com.asus.mobilemanager.MainActivity"))};
    
  • Put these code to your onCreate Method. Here i used shared preference for check it only first time of the app open.

`

final SharedPreferences.Editor pref =    getSharedPreferences("allow_notify", MODE_PRIVATE).edit();    pref.apply(); final SharedPreferences sp =    getSharedPreferences("allow_notify", MODE_PRIVATE)`;


    if(!sp.getBoolean("protected",false)) {
        for (final Intent intent : POWERMANAGER_INTENTS)
            if (getPackageManager().resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {

            AlertDialog.Builder builder  = new AlertDialog.Builder(this);
            builder.setTitle("Alert Title").setMessage("Alert Body")
                    .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            startActivity(intent);
                            sp.edit().putBoolean("protected",true).apply();

                        }
                    })
                    .setCancelable(false)
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                        }
                    })
                    .create().show();
            break;
Milan Pansuriya
  • 2,521
  • 1
  • 19
  • 33
Tharuka Lakshan
  • 348
  • 3
  • 10
  • 2
    Actually when the app's service is running it is considered as running. And this particular receiver ACTION_SCREEN_ON is one which can not receive events when the app does not running even if it is declared in manifest and it must be created in permanently running service. So your answer is good addition to accepted answer but not a complete answer itself. – Sergey Pekar Sep 18 '18 at 12:33
  • Tried in Xiaomi Redmi 6A. App is checked as "autostart" but the service keep killing when I close my app. My service works perfectly in "common" devices, but it cant work in xiaomi devices – Vinícius Carvalho Aug 15 '19 at 01:44
  • At this time they changed their mechanism. I will update my answer for suite your needs. – Tharuka Lakshan Nov 05 '19 at 11:47
  • Can you please explain the code/solution which you have provided? Really difficult to understand why exactly dialog is shown and how things will work. – Virat18 Jul 01 '20 at 10:56
2

If you declare BroadcastReceiver in the Manifest, it will always be active and be called even if the application is closed/stopped

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Abraham
  • 531
  • 5
  • 13
  • 5
    SCREEN_ON and SCREEN_OFF can't be declared in the Manifest file. – alfoks Aug 16 '14 at 14:40
  • @alfoks : why SCREEN_ON and SCREEN_OFF can not be declared in AndroidManifest? Can you explain – Kushal Aug 26 '16 at 07:19
  • @Kushal It is just how they chose to implement it. I don't remember the justification behind this. – alfoks Aug 30 '16 at 10:13
  • 3
    @Kushal because its on the [documentation](https://developer.android.com/reference/android/content/Intent.html#ACTION_SCREEN_ON) :) – Nikiforos Feb 07 '17 at 10:31
  • I am declaring it in the manifest but that does not work if I do not dynamically create the receiver as well. I am trying to receive ACTION_POWER_CONNECTED, ACTION_POWER_DISCONNECTED. – Burak Kaymakci Dec 31 '19 at 09:48
2

Be careful if you are using Android 4.4.x as there is a bug which kills background services when closing the app. I was testing my app in Android 4.4.2 and I had the same problem. Here there is a detailed explanation:

http://www.androidpolice.com/2014/03/07/bug-watch-stopping-apps-on-android-4-4-2-can-silently-kill-related-background-services-a-fix-is-on-the-way/

2

You cannot receive some broadcast events through components declared in manifest.

These events are

  • ACTION_BATTERY_CHANGED
  • ACTION_CONFIGURATION_CHANGED
  • ACTION_SCREEN_OFF (You are playing with this event)
  • ACTION_SCREEN_ON (You are playing with this event)
  • ACTION_TIME_TICK

Reference https://developer.android.com/reference/android/content/Intent.html#ACTION_SCREEN_ON

So in your specific events, you will have to create a Service & you will have to register your event explicitly in service onCreate() with with Context.registerReceiver().

For other events, entry in manifest is sufficient.

1

You could start a service that is running in the foreground. That's the only way to ensure (mostly) that your app will get the events. There is still a chance that your foreground service could get killed in times of crazy memory pressure from the OS (so it's not foolproof). If you start a service in the foreground, the user will see a persistent notification to know that it is always running, though.

So the moral of the story is, do you really need to monitor the screen off/on events at all times? The reason that they force you to register a receiver not in the manifest is that they don't want people to always be monitoring these events and slowing down the device. What are you trying to accomplish?

natez0r
  • 1,064
  • 9
  • 14
0

The best way I found is the Foreground Services. I registered my BroadcastReceiver from my Service only under the onStartCommand() as I want my service needs to run always, I returned START_STICKY

This way, my broadcast receiver survives even after terminating the app from stack.

Used below code in my service

    @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.i("rht", "Received start id " + startId + ": " + intent);
    
            String input = intent.getStringExtra("inputExtra");
            createNotificationChannel();
            Intent notificationIntent = new Intent(this, MainActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(this,
                    0, notificationIntent, 0);
            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("Foreground Service")
                    .setContentText(input)
                    .setSmallIcon(R.drawable.ic_launcher_background)
                    .setContentIntent(pendingIntent)
                    .build();
            startForeground(1, notification);
}

This is how I started my service

Intent serviceIntent = new Intent(this, SpeechServiceForeground.class);
ContextCompat.startForegroundService(this, serviceIntent);
Rohit Mandiwal
  • 10,258
  • 5
  • 70
  • 83