This is related to using Dagger2 to provide a type with params got error "cannot be provided without an @Provides-annotated method" mean
Seems I find a work around (thanks @David Medenjak for explain the error), it works, but not sure if there is better way to do it, especially to provide the flexibility of proving different handling function to the injected InfiniteScrollListener instance. Current solution the handling function is hard coded in the PresentorModule. For that I don't think it is a really solution for the error of
@javax.inject.Named("func") kotlin.jvm.functions.Function0<kotlin.Unit> cannot be provided without an @Provides-annotated method.
Here is the current solution:
The subcomponent with ViewScope, now it provides LinearLayoutManager and InfiniteScrollListenerwhich are singlets to this scope
@ViewScope
@Subcomponent(modules = arrayOf(PresentorModule::class))
interface PresentorComponent {
fun inject (fragment: ArticlesFragment)
fun getPresenter(): Presentor
fun getLinearLayoutManager(): LinearLayoutManager
fun getInfiniteScrollListener(): InfiniteScrollListener
}
The PresentorModule provides linearLayoutManager (has dependency on the current context of this module), and InfiniteScrollListener which depends on the presentor and the linearLayoutManager, with a hard coded implementation of the handling function
@Module
class PresentorModule {
var mContext: Context
var mPresentor: Presentor
constructor(context: Context) {
mContext = context
mPresentor = Presentor()
}
@Provides
@ViewScope
internal fun context(): Context {// not sure if it has to implement to provide this mContext
return mContext
}
@Provides
@ViewScope
fun presentor() : Presentor {
return mPresentor
}
@Provides
@ViewScope
fun linearLayoutManager() : LinearLayoutManager {
return LinearLayoutManager(mContext)
}
@Provides
@ViewScope
fun infiniteScrollListener() : InfiniteScrollListener {
return InfiniteScrollListener(
{
presentor().pullDataFromRemoteServer()
},
linearLayoutManager())
}
// really would like to have the flexibility of letting the consumer provides the
// handling function to the InfiniteScrollListener instance,
// but don’t know how to do it, so have to hard code the handling function
// in the provider of InfiniteScrollListener listed above
// @Provides
// @ViewScope
// fun infiniteScrollListener(func: () -> Unit, layoutManager: LinearLayoutManager) : InfiniteScrollListener {
// return InfiniteScrollListener(func, layoutManager)
// }
}
The definition of InfiniteScrollListener class, which takes a handling function and it is called in onScrolled()
class InfiniteScrollListener (val func: () -> Unit,
val layoutManager: LinearLayoutManager) : RecyclerView.OnScrollListener() {
init{
… …
}
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
… …
func()
… …
}
The use of presentorComponent:
private lateinit var presentorComponent: PresentorComponent
var infiniteScrollListener: InfiniteScrollListener? = null
@Inject
lateinit var presentor: Presentor
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
……
//use dagger to inject viewScope presentor
if (MyApp.graph != null) {
presentorComponent = MyApp.graph
.addChildModle(PresentorModule(getActivity()))
presentorComponent
.inject(this)
}
return view
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
articlesList!!.apply {
setHasFixedSize(true)
clearOnScrollListeners()
infiniteScrollListener = presentorComponent.getInfiniteScrollListener()
// old non-injection way to instantiate the InfiniteScrollListener with
// a custom handling function passed in at this moment
// infiniteScrollListener = InfiniteScrollListener(
// {
// presentor.pullDataFromRemoteServer()
// },
// linearLayout)
addOnScrollListener(infiniteScrollListener)
I really would like to have the flexibility of provide different handling function when inject the class InfiniteScrollListener,
Like the old code was doing:
infiniteScrollListener = InfiniteScrollListener(
{
presentor.pullDataFromRemoteServer()
},
linearLayout)
Instead of hardcoded in the
@Module
class PresentorModule {
… …
@Provides
@ViewScope
fun infiniteScrollListener() : InfiniteScrollListener {
return InfiniteScrollListener(
{
presentor()
},
linearLayoutManager())
}
If I understand you correctly you want the Class that injects your InfiniteScrollListener not to know the LinearLayoutManager but to be in charge of writing the code that is executed?
Alternatively you could inject your InfiniteScrollMethod and give it a setter method for the function.
And then in your View:
Also on a side note: In your dagger Modules there is no need to call the @Provides methods directly. E.g.
Dagger will take care of providing the arguments of the provides method.
Also the function passed into InfiniteScollListener
{presentor()}
will not do anything apart from creating a new presenter when it is called (In case you are not aware and it is not just for the meantime of figuring out your solution).Edit: I think I might have understood what you want to do: You want the Listener to call a function on your presenter or something else, without the view knowing about the code?
How about this:
I think what I am mostly confused about it what you mean with "provide different implementations of the method to the InfiniteScrollListener". Who is in fact the provider of the func?