4

I have a custom WebView that I add to my layout xml:

<my.company.ui.ExtendedWebView />

It extends the native WebView:

class ExtendedWebView @JvmOverloads constructor(context: Context, 
    attrs: AttributeSet? = null,  defStyle: Int = 0) 
    : WebView(context, attrs, defStyle) {
// ...
}

How can I use Hilt to inject a @Singelton class in to the class above? Property injection? How should I annotate the class?

filur
  • 2,116
  • 6
  • 24
  • 47

2 Answers2

8

What I have found is that the @AndroidEntryPoint annotation needs to be on the View, the Fragment (if in a Fragment) AND the Activity. Because Annotations.

So consider you have your DI set up as follows:

/* CONTENTS OF com.org.app.di/dependencyModule.kt */
@Module
@InstallIn(ViewComponent::class)
object DependencyModule {
    @Provides
    fun provideDependency(@ApplicationContext context: Context): DependencyType
            = DependencyInstance(context)
}

And my Application is properly set up:

@HiltAndroidApp
class SuperAwesomeApplication : Application()
/* Remember to reference this is the manifest file, under the name attricbute! */

Now if I have a View with an injected dependency:

@AndroidEntryPoint
class SuperAwesomeView(context: Context, attrs: AttributeSet) : View(context, attrs) {
    @Inject
    lateinit var dependency: DependencyType
    ...

I will get the error:

...
Caused by: java.lang.IllegalStateException: class com.app.org.ui.view.SuperAwesomeView, Hilt view must be attached to an @AndroidEntryPoint Fragment or Activity.
...

So I added the @AndroidEntryPoint annotation to the Fragment containing the View:

@AndroidEntryPoint
class SuperAwesomeFragment : Fragment() {
...

And then we get hit with the next error:

 Caused by: java.lang.IllegalStateException: Hilt Fragments must be attached to an @AndroidEntryPoint Activity. Found: class com.org.ui.SuperAwesomeActivity

And so I've learned the annotations need to bubble all the way up, from View to (if in a Fragment) Fragment, to Activity:

@AndroidEntryPoint
class SuperAwesomeActivity : AppCompatActivity() {
...
Quintin Balsdon
  • 5,484
  • 10
  • 54
  • 95
2

Let's say your singleton class currently looks like this:

class ExampleSingletonClass( //... some dependencies) {
     //.. some other stuff
}

To make it a Singleton change it to:

@Singleton
class ExampleSingletonClass @Inject constructor( //... some dependencies) {
     //.. some other stuff
}

Then, to inject it into your ExtendedWebView do this:

class ExtendedWebView @JvmOverloads @Inject constructor(
    context: Context, 
    attrs: AttributeSet? = null,  
    defStyle: Int = 0,
    private val exampleSingleton: ExampleSingletonClass // your singleton, doesn't need to be private.
   ) : WebView(context, attrs, defStyle) {
// ...
}

You don't need any @AndroidEntryPoint here, but your Fragment / Activity needs @AndroidEntryPoint

Andrew
  • 4,264
  • 1
  • 21
  • 65
  • Thanks. If I do that I get `Types may only contain one @Inject constructor`. Should I remove the default parameters? – filur Oct 09 '20 at 18:45
  • 1
    @filur Either remove the default parameter or create a second constructor with the default parameter and let it point to the first constructor. Do `@Inject` on the second constructor as well. You can refer to [this](https://stackoverflow.com/questions/55240472/types-may-only-contain-one-inject-constructor) – Andrew Oct 09 '20 at 18:48
  • Gotcha. Could you please post a sample with the 2 constructor approach? – filur Oct 09 '20 at 19:00
  • @filur I'm sorry but I saw that this is unfortunately not possible (at least how I tried to). Kotlin can't distinguish between two constructors just because one has a default value and the other not. It says `Conflicting overloads`. My solution: Just remove the default parameters if you can and if not, you have to live with the warning `Types may only contain one @Inject constructor` – Andrew Oct 10 '20 at 08:55
  • Thanks, I suppose removing them wont hurt in this case, since the framework hopefully sets them. – filur Oct 10 '20 at 10:30
  • @Andrew Also have a look at my question – Taimoor Khan Aug 16 '23 at 11:44