简体   繁体   中英

Android Dagger-Hilt How to do Field Injection in abstract class?

I am using Dagger-Hilt for Dependency Injection, and I am stuck with not knowing how to do field injection inside an abstract class.

// @ViewModelScoped
abstract class BaseUseCase<Params, Return>{
    // lateinit var not initiazlied. Cannot be injected
    @Inject
    lateinit var errorHandler: ErrorHandler

    fun execute(@nullable params: Params?=null): Flow<DataState<Return>> = flow {
        emit(Datastate.Loading)
        emit(executeRealization(params))
        ...
    }.catch{ e->
        when(e){
            ...
            is Exception -> {
                ...
                errorHandler.handleError(e.message ?: "Unknown Error")
            }
        }
    }

    protected abstract fun executeRealization(@Nullable params: Params?=null): DataState<Return>

}

[DI package]

  • I provided "ErrorHandler" as a singleton using dagger-hilt (AppModule.kt)
  • Usecases which extend above BaseUseCase are all written for dagger-hilt (UseCaseModule.kt)
  1. I tried providing or binding BaseUseCase class using dagger-hilt such as BaseUseCaseModule.kt, however since it has type parameters, it cannot be binded and also provided.

  2. Currently i cannot inject errorHandler inside BaseUseCase class, so just written ErrorHandler 'object' and using it statically. (eg Object ErrorHandler {})

Question

  1. How to do field injection inside abstract class?
  2. Or Am i missing something?

How to do field injection inside an abstract class?

This is currently not supported.

You can consider refactoring your code in these two approaches.

First Approach

Move the exception/error handling up the chain towards the UI, this would include the approach of ViewModel.

With this, you can constructor inject your error handler, then execute your UseCase and wrap the handler around it.

Let's look at a possible solution, in the sketch, we'll utilize clean architecture approach;

ViewModel.kt

@HiltViewModel
class YourViewModel @Inject constructor(private val errorHandler: ErrorHandler, private val useCase : SpecificUseCase) : ViewModel(){

suspend fun realizationFunction(params : Params?=null) : Flow<DataState<Return>> = flow {
    emit(Datastate.Loading)
    try{
       emit(useCase(params))
    }catch(exception : Exception){
       errorHandler.handleError(e.message ?: "Unknown Error")
    }

}
}

On your specific useCase, I do recommend you use repository pattern to execute your functions in order to separate concerns instead of executing your functions inside the use case.

Second Approach

This approach involves taking the error handler deeper into the chain and constructor injecting your error handler in your repository implementation.

This would give you the chance to run the particular function/service calls inside a try/catch and handle your error in there.

The con of this second approach may include the challenge of returning the error result, but incorporating a resource class will make it seamless - seems like you have one already, DataState .

class YourRepositoryImpl(private val errorHandler: ErrorHandler) : YourRepositoryInterface {
  override suspend fun doSomething(params : Params?) : Flow<DataState<Return>> {
   //call your function and use the error handler 
}
}

This gives you cleaner code and better error handling.

Or Am I missing something?

You may be interested in reading much about app architecture and separation of concerns.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM