Initializing Retrofit client in application - android

Like below?
val retro: Retro by lazy {
PilotApp.retro!!
}
class PilotApp : Application() {
companion object {
var retro: Retro? = null
}
override fun onCreate() {
retro = Retro(applicationContext)
super.onCreate()
}
}
Is this a good way of initialisation? Thanks in advance.

This is not following the rule of "Inversion of Control" and It is not a good idea.
The reason it is not a good idea is because whenever a class (like a ViewModel, Fragment or Activity or ...) wants to use the retro, they have to get the Retro object themselves by calling your first line (PilotApp.retro).
The alternative (called Dependency Injection/Inversion of Dependency) is that the Retro object is given to the class (again, the ViewModel or whatever) when it is initialized.
The reason why this is important is because with the second approach, you can make your classes that use the Retro, testable. You can give them RetroMock or TestRetro that does what you want (for example, mock an api to return an error).
Another note for your example, you don't need to make the retro nullable, you should make your var a lateinit and make it non-null.

Related

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)
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.
Currently i cannot inject errorHandler inside BaseUseCase class, so just written ErrorHandler 'object' and using it statically. (e.g. Object ErrorHandler {})
Question
How to do field injection inside abstract class?
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.

Is it okay to use LiveData objects inside a service?

I am using Companion object in service to expose my LiveData to a fragment. Is this okay to use or will it cause me problems like memory leaks?
In my service:
companion object {
val timeLeftInSeconds = MutableLiveData<Long>(0)}
In my fragment:
LockoutService.timeLeftInSeconds.observe(viewLifecycleOwner, Observer {...})
No it's fine because companion object is kinda like static fields, but I highly recommend to use a repository instead because it will increase you code readability and makes it more robust. Something like
object AppRepository{
val timeLeftInSeconds = MutableLiveData<Long>(0)}
}
And in fragment
AppRepository.timeLeftInSeconds.observe(viewLifecycleOwner
No it's totally alright because companion objects are like static properties in java and are not bound to the class you define them in.
Also you can put it in the same file, outside of your service
LockoutService.kt
val timeLeftInSeconds = MutableLiveData<Long>(0)}
class LockoutService {...}
And access it without mentioning the service name

Koin: How to get the same Instance accoding to a given key

I used ViewModelProvider(this).get(myDataIdentifier, MyViewModel::class.java) to get the same viewmodel for each identifier.
Now I want to use Koin for dependency injection but I can't figure out how to get this working.
I can inject data via val viewModel by viewModel() but where am I able to make sure to get the same instance, identified by myDataIdentifier? I can't wrap my head around qualifier, parameter,....
Sorry, maybe this is a dumb question and i just overlooked something.
Try with named components, name your viewmodel (this may now need to be a singleton?)
val myModule = module {
viewModel(named("myViewModel")) { MyViewModel() }
}
...
val viewModel: MyViewModel by viewModel(named("myViewModel"))
https://engineering.bigshyft.com/koin-2-0-for-android/

Use Companion object in kotlin android is a good practice?

I have ever used Java to programming in android, but a few weeks ago I started to learn kotlin, when I use Java I tried to use object oriented approach and use the less possible static objects or instances, so when I see some materials in internet about some implementations of consume web services in kotlin I see something like this:
/*call of method from activity*/
val message = WebServiceTask.getWebservice(getString(R.string.url_service))
/*Class to do the call to webservice*/
class WebServiceTask {
companion object {
fun getWebService(url: String): WebServiceResponse {
val call =
RetrofitInstance.getRetrofit(url).create(ApiService::class.java).getList()
.execute()
val webServiceResponse = call.body() as WebServiceResponse
return user
}
}
}
/*Class to get Retrofit instance*/
class RetrofitInstance {
companion object{
fun getRetrofit(url: String): Retrofit {
return Retrofit.Builder()
.baseUrl(url)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
}
Like you see i use companion object in two classes and according to i read companion object is equivalent to static instance in java, so my question is:
is this code following object oriented programming?, this aproach is recommended?, in case that answer is no and which is the best implementation for this code in object oriented
Yes, companion object is Kotlin's equivalent of static members in Java. Everything that applies to static, applies to companion object as well.
The use of companion object depends on how it interacts with the state of class's object.
If you are using some methods which are totally Pure functions Or Some final values which you need to provide access to outside the class itself, in that case using companion object makes total sense.
It is recommended for the above conditions because it does not interfere with the state of the class's object.
So, for the given code snippet it is a valid use of companion object.
Observe that methods inside companion object do not interact with something which is not passed to them as parameters. Everything that you see is created/initialized or used inside the methods only, Just the result it gets out.
Note:
However, if your companion object members(values or functions) interfere with the state of the object, it will cause leaks, which will lead you to troubles you have never faced before.
Yes, it is equivalent to static. No, it is not recommended, as it leads to problems with mocking for testing, for example.

Kotlin extension function with request to rest server

I'm setting up extension function for Timber. I want to have kind of function to send log to my server.
The problem for me is Dagger. I have instance of RestService class in dagger and I'm using it in my whole app.
But to use it I need inject somewhere this RestService. I can't do it in constructor because I haven't it.
I want to have something like this:
fun Timber.serverLogDebug(log: String) {
restService.log(log)
}
Is it probably at all? It will be convenience for me to use my mechanism like simple Timber.d().
Alternatively I can call
restService.log(log)
in every place. But I have to have this instance everywhere.
In the file where you define the extension function, also define a "singleton" object to hold your restService instance, create a setter for it, and reference it from the logger function.
private object ServiceHolder {
var restService: RestService
}
fun Timber.setRestService(restService: RestService) {
ServiceHolder.restService = restService
}
fun Timber.serverLogDebug(log: String) {
ServiceHolder.restService.log(log)
}
Now you can "statically inject" your service instance by calling Timber.setRestService where you plant your Timber DebugTree.
Note: If you want to log to the server every time you log (or every time you log an event of a specific level), you might be better off creating a custom Timber.Tree.

Categories

Resources