0

I have a BroadcastReceiver which will handle some actions once boot has been completed. However, the methods that will be invoked from my onReceive method require passing an Activity:

if (intent!!.action.equals(Intent.ACTION_BOOT_COMPLETED)) {
    Log.i(LOG_TAG, "onReceive (ACTION_BOOT_COMPLETED)");
    doSomeActionWithActivity(activity);
}

In order to do this, I've created a constructor for my receiver (which I've called BootReceiver:

public BootReceiver(Activity activity) {
    this.activity = activity;
}

But although this would resolve my initial issue, I got an error from the manifest file:

.BootReceiver has no default constructor

To resolve this issue, I decided to register and unregister the receiver programmatically, so that I can pass Activity to it.

In onCreate():

Log.i(LOG_TAG, "Registering the receiver (BootReceiver)");
mReceiver = new BootReceiver(SplashActivity.this);
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
registerReceiver(mReceiver, intentFilter);

And, in onDestroy():

if (mReceiver != null) {
    Log.i(LOG_TAG, "Unregistering the receiver (BootReceiver)");
    unregisterReceiver(mReceiver);
}

After testing my app again, I got the following error:

java.lang.IllegalArgumentException: Component class ...BootReceiver does not exist in ...

pointing to a part of my code where I do this:

ComponentName receiver = new ComponentName(context, BootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
        PackageManager.DONT_KILL_APP);

In the above code, I want to be restarting any alarms set when the device reboots. Also, I'm using this file from Remindly, an open-source app as a guide/reference, for the above code.

What would be the correct way of restarting alarms and being able to pass Activity to my BootReceiver class.

Farbod Salamat-Zadeh
  • 19,687
  • 20
  • 75
  • 125
  • Why does your Receiver need an `Activity` instance? Are you sure you can't do whatever you need to do using the `Context` passed into `onReceive()` instead? – Mike M. Aug 31 '16 at 10:57
  • @MikeM. In `onReceive()`, I am calling some methods from other parts of my code which require `Activity`. In most cases, these methods need `Activity` to use as `Context` and to do `.getApplication()` to use `Application`. – Farbod Salamat-Zadeh Aug 31 '16 at 11:00
  • Well, calling an `Activity` instance's methods directly from a Receiver is not good design. Also, as mentioned, there's a `Context` passed into `onReceive()`. Use that instead. There's not going to be an active `Activity` instance at boot. Even if there was, the system handles that static Receiver instantiation, and it's not going to use your overloaded constructor. – Mike M. Aug 31 '16 at 11:03
  • @MikeM. Ah, thanks. Also, if I require `Application` in these methods (recall I use `Activity` both for `Context` and to use `.getApplication()`), how would I pass an instance of `Application`? – Farbod Salamat-Zadeh Aug 31 '16 at 11:05
  • @MikeM. I cannot use `getApplication()` on `Context` (it doesn't exist), but I can do `getApplicationContext()`. However, I read from [this SO answer](http://stackoverflow.com/a/6760019/4230345): _when writing code in a broadcast receiver, which is not a context but is given a context in its `onReceive()` method, you can only call `getApplicationContext()`. Which also means that you are not guaranteed to have access to your application in a BroadcastReceiver._ Does this mean I have no other option but to use `getApplicationContext()` and cast it? – Farbod Salamat-Zadeh Aug 31 '16 at 11:14
  • @MikeM. The answer below suggests using setters to pass my `Activity` instance. Would this work? Does `onReceive()` get invoked before any activities are created? – Farbod Salamat-Zadeh Aug 31 '16 at 11:16
  • Oop, yeah, sorry. I forgot `getApplication()` is a member of `Activity`. Yeah, you'll have to use `getApplicationContext()`. As for the answer below, the system instantiates that Receiver, and you won't really have any access to it outside of it, especially before it runs, so you'd never have a chance to call any setter methods on it. – Mike M. Aug 31 '16 at 11:26
  • I think you need to refactor. Why do you need the `Application` class specifically? – Shaishav Aug 31 '16 at 11:29
  • @Shaishav That's what I will be doing. I need the `Application` in order to access variables and methods that apply for the whole application, not just one activity. – Farbod Salamat-Zadeh Aug 31 '16 at 11:36
  • Yeah, I agree with Shaishav. I just realized that's probably the reason you need the `Application`'s `Context` there, and came back to suggest moving your global stuff out of an `Application` subclass. – Mike M. Aug 31 '16 at 11:50
  • @MikeM. I've refactored my methods so that they take `Context` and `Application` instead of `Activity` - this seems to work better for me. If you put your solution as an answer I could accept it if you want... – Farbod Salamat-Zadeh Aug 31 '16 at 19:33

1 Answers1

0

If you are sure that the problem is caused by a constructor with arguments, you can use setter instead of arguments in constructor.

Vladyslav Matviienko
  • 10,610
  • 4
  • 33
  • 52
  • Thanks for your answer. If I was to write my `BootReceiver` class with no arguments and declare it in `AndroidManifest.xml` (not requiring me to register and unregister it programmatically), at which point would I call the setter? Would I invoke the setter in the `onCreate()` of the first `Activity` that is run in my app? – Farbod Salamat-Zadeh Aug 31 '16 at 11:03