14

How can I make it possible to pass database Instance to the MyFirebaseMessagingService class which extends FirebaseMessagingService so that I can save data payload locally?

Note: I already setup dagger 2 in my app, it's working perfectly.

Below is MyFirebaseMessagingService class:

class MyFirebaseMessagingService @Inject constructor(exampleOneDao: ExampleOneDao?) : FirebaseMessagingService() {

    override fun onMessageReceived(remoteMessage: RemoteMessage?) {
    //need db instance to store data payload locally (Room)
    }
}

And below is the AppModule class of Dagger 2

@Module(includes = arrayOf(ViewModelModule::class))
class AppModule() {

    // --- DATABASE INJECTION ---
    @Provides
    @Singleton
    internal fun provideDatabase(application: Application): MyDatabase {
        return Room.databaseBuilder(application,
               MyDatabase::class.java, "MyDatabase.db")
              .build()
    }

    @Provides
    @Singleton
    internal fun provideExampleOneDao(database: MyDatabase): ExampleOneDao {
        return database.exampleOneDao()
    }

    @Provides
    @Singleton
    internal fun provideMyFirebaseMessagingService(exampleOneDao: 
        ExampleOneDao): MyFirebaseMessagingService {
           return MyFirebaseMessagingService(exampleOneDao)
    }
}

Is it possible to provide database and dao to MyFirebaseMessagingService class?

I tried above method to provide exampleOneDao to the MyFirebaseMessagingService class, but it throws the following Exception

MyFirebaseMessagingService: java.lang.InstantiationException: java.lang.Class<com.example.user.app.firebase.messaging.MyFirebaseMessagingService> has no zero argument constructor

Thank You.

Vadim Kotov
  • 7,103
  • 8
  • 44
  • 57
Sharoon Amjid
  • 547
  • 6
  • 15

3 Answers3

29

Finally got the solution from this link: https://github.com/googlesamples/android-architecture-components/issues/253

As MyFirebaseMessagingService is a Service class, so for injection in Service class, Dagger provides a way through which we can inject dependencies into Service class. Below are the steps to enable injection in service class:

1) Make Application implements HasServiceInjector and inject a DispatchingAndroidInjector for services.

public class App extends Application implements HasActivityInjector, HasServiceInjector {

    @Inject
    DispatchingAndroidInjector<Activity> dispatchingActivityInjector;

    // Add this line
    @Inject
    DispatchingAndroidInjector<Service> dispatchingServiceInjector;

    @Override
    public void onCreate() {
        super.onCreate();
        AppInjector.init(this);
    }

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return dispatchingActivityInjector;
    }

    // override this method after implementing HasServiceInjector
    @Override
    public AndroidInjector<Service> serviceInjector() {
        return dispatchingServiceInjector;
    }

}

2) Create a new module to perform injection over your services.

@Module
abstract class ServiceBuilderModule {

    // for my case, the service class which needs injection is MyFirebaseMessagingService
    @ContributesAndroidInjector
    abstract MyFirebaseMessagingService contributeMyFirebaseMessagingService();

}

3) Register your new module in your application's component.

@Component(modules = {
        AndroidSupportInjectionModule.class,
        AppModule.class,
        ActivityBuilderModule.class,
        // Need to define previously created module class here
        ServiceBuilderModule.class
})
@Singleton
public interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(App application);
        AppComponent build();
    }
    void inject(App app);
}

4) And finally, override method onCreate of the service adding AndroidInjection.inject(this).

public class MyFirebaseMessagingService extends FirebaseMessagingService {

    //So now we are able to inject here same as we do in Activity. No need for constructor injection
    @Inject ExampleOneDao exampleOneDao

    // Override this method first
    @Override
    public void onCreate() {
        AndroidInjection.inject(this);
        super.onCreate();
    }

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        // use your dao here to store remoteMessage data payload into your database, e.g exampleOneDao.save(somethingHere)
    }


}
Sharoon Amjid
  • 547
  • 6
  • 15
  • It doesn't work in Kotlin, it triggers the error No injector factory bound for Class. Injector factories were bound for supertypes of com.fuentesoft.messagingapp.service.MessageService: [com.google.firebase.messaging.FirebaseMessagingService]. Did you mean to bind an injector factory for the subtype? – omensight Apr 22 '19 at 16:27
  • @MijaelViricochea This is because you need to set an injector factory i.e. you need to tell the Injector that this service class is one of the injection targets. You can do this by creating a new module class like: ``` @Module abstract class ServiceModule { @ContributesAndroidInjector(modules = []) internal abstract fun bindService(): MessageService } ``` And make sure to include ServiceModule when you declare the component. This error will disappear then. – Saifur Rahman Mohsin Jan 11 '20 at 22:11
3

Here is the Kotlin implementation:

Application.kt

@Inject
lateinit var dispatchingServiceInjector: DispatchingAndroidInjector<Service>

override fun serviceInjector(): AndroidInjector<Service> {
    return dispatchingServiceInjector
}

Module.kt

@Module
abstract class FirebaseServiceModule {
    @ContributesAndroidInjector
    abstract fun contributeMyFirebaseMessengingService(): MyFirebaseMessengingService
}

Component.kt

@Component(modules = [FirebaseServiceModule::class])
interface Component {
    ...
}

MyFirebaseMessengingService.kt

override fun onCreate() {
    AndroidInjection.inject(this);
    super.onCreate()
}
sonique
  • 3,402
  • 2
  • 20
  • 33
0

The injection of dependencies with Dagger into services has to be through public class variables and not from the constructor to avoid type error MyFirebaseMessagingService has no zero argument constructor.

After implementing the solution pass

From:

class MyFirebaseMessagingService @Inject constructor(exampleOneDao: ExampleOneDao?) : FirebaseMessagingService() {

    override fun onMessageReceived(remoteMessage: RemoteMessage?) {
    //need db instance to store data payload locally (Room)
    }
}

To:

class MyFirebaseMessagingService : FirebaseMessagingService() {

@Inject 
private lateinit var exampleOneDao: ExampleOneDao? 

    override fun onMessageReceived(remoteMessage: RemoteMessage?) {
    //need db instance to store data payload locally (Room)
    }
}

GL

Source

Braian Coronel
  • 17,823
  • 4
  • 30
  • 33