How to inject into a BroadcastReceiver
Asked Answered
D

8

35

Does someone already had to inject an already existing class, with some business logic, into a BroadcastReceiver using dagger?

I'm using dagger 1 and already found a nice example (https://github.com/adennie/fb-android-dagger) but, I could not find how we can add an already existing class, which belongs to a different module, into a BroadcastReceiver.

Any help or advice would be greatly appreciated.

Deliladelilah answered 30/11, 2015 at 14:59 Comment(0)
D
3

I managed to inject use cases into my Broadcast by defining a Module which provide the use cases I need and I add the Module on the onReceive method, check the code below:

My BroadcastReceiverModule:

@Module(injects = { MyBroadcastReceiver.class }, addsTo = MyAppModule.class)
public class BroadcastReceiverModule {
    @Provides @Singleton MyUtilsClass providesMyUtilsClass(MyUtilsClassImpl myUtilsClass) {
        return myUtilsClass;
    }
    @Provides @Singleton MyUseCase providesMyUseCase(MyUseCaseImpl myUseCaseUtils) {
        return myUseCaseUtils;
    }
}

My BroadCastReceiver:

@Inject MyUtilsClass myUtilsClass;
@Inject MyUseCase myUseCase;
@Override public void onReceive(Context context, Intent intent) {
    AcidApplication.getScopedGraph(getModules().toArray()).inject(this);
    myUseCase.call();
    myUtilsClass.doSomething();
}
protected List<Object> getModules() {
    List<Object> result = new ArrayList<>();
    result.add(new BroadcastReceiverModule());
    return result;
}
Deliladelilah answered 19/2, 2016 at 18:2 Comment(1)
Note - this is Dagger 1.Lenoralenore
F
31

Dagger 2 example for injecting objects into a BroadcastReceiver.

The BroadcastReceiverModule.kt

@Module
abstract class BroadcastReceiverModule {
    @ContributesAndroidInjector
    abstract fun contributesMyTestReceiver() : MyTestReceiver
}

The AppComponent.kt

@Singleton
@Component(
        modules = [
            (AndroidSupportInjectionModule::class),
            (BroadcastReceiverModule::class)
        ])
interface AppComponent : AndroidInjector<MyApp> {
    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<MyApp>()
}

The Application class

class MyApp : DaggerApplication() {
    override fun applicationInjector(): AndroidInjector<MyApp> =
            DaggerAppComponent.builder().create(this@MyApp)
}

The BroadcastReceiver class

class MyTestReceiver : BroadcastReceiver() {

    @Inject
    lateinit var anInjectedObject: MyInjectObject

    override fun onReceive(context: Context, intent: Intent) {
        AndroidInjection.inject(this, context)
        anInjectedObject.doSomthing()
    }
}
Flighty answered 15/3, 2019 at 3:21 Comment(2)
This is the correct way to do this in 2019. However, you can save the last two steps by simply having your Application class extend DaggerApplication and your Receiver extend DaggerBroadcastReceiver.Leach
@Flighty is it also possible to define the BroadcastReceiverModule as an interface? Could you please explain why did you choose an abstract class for this? Thank you very much in advance.Warrin
I
29

Same as you inject to an Activity

public void onReceive(Context context, Intent intent) {
        ((Application) context.getApplicationContext()).getInjector().inject(this);
    }
Ilmenite answered 3/7, 2016 at 0:32 Comment(1)
Are we guaranteed that Application Context will always be an Application instance? If not, this is not an acceptable answer.Autoroute
M
23

It might be too late to answer this question, but I will provide an example from my recent project where I tried to inject AppWidgetProvider which is a direct subclass of BroadcastReceiver.

We need to inject a retrofit service into a BroadcastReceiver:

@Module
public class NetModule {
    /** shrunk for simplicity's sake. **/
    @Singleton
    @Provides
    public WidgetService provideWidgetService(Application application, OkHttpClient client, Gson gson) {
        return new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .baseUrl(application.getString(R.string.api_url))
                .client(client)
                .build()
                .create(WidgetService.class);
    }
}

Create another abstract @Module for the with abstract methods annotated with @ContributesAndroidInjector that return BroadcastReceivers you want to inject:

/**
 * To inject the app widgets.
 */
@Module
public abstract class WidgetsModule {
    @ContributesAndroidInjector
    abstract IngredientsWidget contributesIngredientsWidget();
}

If you forgot to add this module, you will get error like:

java.lang.IllegalArgumentException: No injector factory bound for Class<>

Then the component with both modules, besides AndroidInjectionModule

@Singleton
@Component(modules = {AndroidInjectionModule.class, NetModule.class, WidgetsModule.class})
public interface AppComponent {
    void inject(RecipesApp recipesApp);
}

Then in your Application class you implement HasBroadcastReceiverInjector interface.

public class RecipesApp extends Application implements HasBroadcastReceiverInjector {

    @Inject
    DispatchingAndroidInjector<BroadcastReceiver> broadcastReceiverInjector;

    @Override
    public void onCreate() {
        super.onCreate();

        component().inject(this);
    }

    public AppComponent component() {
        return DaggerAppComponent.builder()
                .build();
    }

    @Override
    public AndroidInjector<BroadcastReceiver> broadcastReceiverInjector() {
        return broadcastReceiverInjector;
    }
}

Finally, you can inject your BroadcastReceiver inside onReceive() before calling the super().

public class IngredientsWidget extends AppWidgetProvider {

    @Inject
    public WidgetService api;


    @Override
    public void onReceive(Context context, Intent intent) {
        /** Don't forget this line **/
        AndroidInjection.inject(this, context);
        super.onReceive(context, intent);
    }

}

You can find more about how to inject android components docs.

I built a small sample: broadcast-injection.

Marcimarcia answered 1/11, 2017 at 15:31 Comment(2)
Any Idea how to test that ?Dipterocarpaceous
That's the kind of Stackoverflow answers I like: explanation with examples, link to the docs and a example project on GitHub - thanks @Marcimarcia !Krieg
V
5

just a little too late but I have a slight improvement over the previous answers, you don't need to explicitly call AndroidInjection.inject(this, context); in onReceive method.

Here is the receiver so you don't have to do that, the trick is extending from DaggerBroadcastReceiver:

class MyReceiver : DaggerBroadcastReceiver() {

    @Inject
    lateinit var repository: MyRepository

    override fun onReceive(context: Context?, intent: Intent?) {
        super.onReceive(context, intent)
        // Do your stuff...
    }
}

The rest is just the same registering the modules, etc. I hope it helps :D

Veolaver answered 20/1, 2021 at 15:2 Comment(0)
O
4

You can use DaggerBroadcastReceiver or you can override onReceive method like below:

public void onReceive(Context context, Intent intent) {
    AndroidInjection.inject(this, context);
    // your code should be here ...
}

dagger documents: https://dagger.dev/api/2.24/dagger/android/DaggerBroadcastReceiver

Orobanchaceous answered 18/9, 2019 at 20:5 Comment(0)
D
3

I managed to inject use cases into my Broadcast by defining a Module which provide the use cases I need and I add the Module on the onReceive method, check the code below:

My BroadcastReceiverModule:

@Module(injects = { MyBroadcastReceiver.class }, addsTo = MyAppModule.class)
public class BroadcastReceiverModule {
    @Provides @Singleton MyUtilsClass providesMyUtilsClass(MyUtilsClassImpl myUtilsClass) {
        return myUtilsClass;
    }
    @Provides @Singleton MyUseCase providesMyUseCase(MyUseCaseImpl myUseCaseUtils) {
        return myUseCaseUtils;
    }
}

My BroadCastReceiver:

@Inject MyUtilsClass myUtilsClass;
@Inject MyUseCase myUseCase;
@Override public void onReceive(Context context, Intent intent) {
    AcidApplication.getScopedGraph(getModules().toArray()).inject(this);
    myUseCase.call();
    myUtilsClass.doSomething();
}
protected List<Object> getModules() {
    List<Object> result = new ArrayList<>();
    result.add(new BroadcastReceiverModule());
    return result;
}
Deliladelilah answered 19/2, 2016 at 18:2 Comment(1)
Note - this is Dagger 1.Lenoralenore
B
2
  • Previous solutions don't work anymore now in the latest Hilt versions as Hilt does not have any function called AndroidInjection.
  • App Widget Provider extends the Broadcast Receiver. So you can simply annotate any Broadcast receiver like this:

Answer:

@AndroidEntryPoint
class StandardWidgetProvider : AppWidgetProvider() {

    @Inject
    lateinit var room: AppDatabase

    override fun onReceive(context: Context, intent: Intent?) {
        super.onReceive(context, intent)
        // do your stuff here
    }
}
Brimstone answered 23/1, 2022 at 1:30 Comment(0)
N
1

In KOIN Dependency Injection,

class ServiceClass1()
class LazyServiceClass2()

// In Activity no need for KoinComponent

class MyActivity: AppCompatActivity() {

    // evaluates dependency eagerly
    val service: ServiceClass1= get()
    
    // evaluates dependency lazily
    val lazyService: LazyServiceClass2by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
             
        // does something with services
    }

}

But In the case of Receiver, extend "KoinComponent" like below

// In non-lifecycle component, KoinComponent is needed
class SomeBroadcastReceiver: BroadcastReceiver(), KoinComponent {
    
    // evaluates dependency eagerly
    val service: Service1 = get()
    // evaluates dependency lazily
    val lazyService: LazyService2 by inject()
    
    override fun onReceive(context: Context?, intent: Intent?) {
        // does something with services
    }

}

Hope it will work for you.

Nubilous answered 26/3, 2022 at 12:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.