Search code examples
javaandroidkotlindependency-injectiondagger-2

Use AppComponent.Inject() in different activities (Dagger2)


I have two activities that uses ViewModelProvider, which must be injectied with Dagger.

To provide injection in an activity, I must call appComponent.inject() method. I can make it to MainActivity. But I can't use it in another activities (obviously because appComponent.inject() takes MainActivity instance as an argument).

So, the question is: What shall I do with AppComponent (or with smth. else), to be able to get instance of AppComponent from different activities (not only from MainActivity).

P.S. Possibly, it's something about Dagger Scopes, but I can't figure out what exactly to do.

AppComponent.kt

...
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {

    fun inject(mainActivity: MainActivity) 

    @Component.Builder
    interface Builder {

        @BindsInstance
        fun application(application: Application): Builder

        fun create(): AppComponent
    }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var viewModelProvider: Provider<MainActivityViewModel.Factory>

    private val viewModel: MainActivityViewModel by viewModels { viewModelProvider.get() }

    private lateinit var recyclerView : RecyclerView

    private val adapter by lazy(LazyThreadSafetyMode.NONE) {
        RecyclerViewAdapter(this) 
}

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        appComponent.inject(this)

        setContentView(R.layout.activity_main)

        setupViewModel()
        setupUI()
    }

    private fun setupViewModel(){
        viewModelProvider.get()
        viewModel.setQuery("Apple")
    }

    private fun setupUI(){
        recyclerView = findViewById(R.id.recyclerView)

        GridLayoutManager(this,3, RecyclerView.VERTICAL,false
        ).apply {
            recyclerView.layoutManager = this
        }

        recyclerView.adapter = adapter
        addRepeatingJob(Lifecycle.State.STARTED) {
            viewModel.images.collectLatest(adapter::submitData)
        }
    }
}

ViewPager.kt


class ViewPagerActivity : AppCompatActivity() {

    @Inject
    lateinit var viewModelProvider: Provider<MainActivityViewModel.Factory>
    private val viewModel: MainActivityViewModel by viewModels { viewModelProvider.get() }

    private lateinit var viewPager: ViewPager2

    private val adapter by lazy(LazyThreadSafetyMode.NONE) {
        ViewPagerAdapter(this) 
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_view_pager)
        Log.d("dbg", intent.getIntExtra("ARG", 0).toString())

        setupViewModel() 
        setupUI();
    }

    private fun setupUI()
    {
        viewPager = findViewById(R.id.viewPager)

        viewPager.adapter = adapter
        addRepeatingJob(Lifecycle.State.STARTED) {
            viewModel.images.collectLatest(adapter::submitData)
        }

    }

    private fun setupViewModel()
    {
        viewModelProvider.get()
    }

}

Thanks.


Solution

  • AppComponent's Builder takes an Application. You haven't specified how you create or store AppComponent, but presumably it's either in the Application instance or in a static field to preserve its @Singleton property.

    In that case, you can create a second inject method, named similarly or differently, which takes your SecondActivity:

        fun inject(mainActivity: MainActivity)
    
        fun inject(secondActivity: SecondActivity) 
    

    Each of those is a members injection method: Dagger will inspect the object at compile time, figure out what is labeled with an @Inject annotation, and call the methods and populate the fields when you call inject. You can use the same component for this or use a different one.

    Eventually, if you want to be able to inject the same instance within an Activity but different instances between different Activities, you may look into a second component (possibly a subcomponent) with a scope annotation like @ActivityScope that you would write. You could also look into adopting Hilt, which would manage some of these scopes for you.