Hello! Is there any good way to pass the applicati...
# android
c
Hello! Is there any good way to pass the application context to an object? I have a service that interfaces with an API. I think it would be ok for it to be a singleton, as it looks more like a manager than a generic class. Should I create a method
fun init(application: Application) {//do stuff}
or is there a better way?
😜 1
a
what API?
i
Create custom application class
Copy code
class MyApplication : Application() {
    lateinit var context:Context
    override fun onCreate() {
        super.onCreate()

        this.context = context
    }
}
Now context is staticly available through
MyApplication.context
However it would be wiser (from testing perspective) to inject this context into the object then accessing it directly using
MyApplication.context
c
@Alan Evans Why, does it matter?
@igor.wojda Do you mean injection through a function like I did, as a parameter?
a
APIs have instructions. If you have an API that needs Application and that API's instructions calls for a one time initialization at the start of the Application, then singleton should be suitable.
i
Injection as some dependency injection framework that you use widely across the app (Dagger/Koin/Kodein) so context will be injected when object is created (or in case of android object when activity/service reach proper lifecycle state). However if you don’t use injection in you app then passing it as a function param would be a way to go (definitely better then accessing it directly from custom static application class)
c
@Alan Evans I just need the application context because that object saves a picture obtained in retrofit GET to a file. I'm probably going at it the wrong way, I'm discovering all that ^^
a
yes, this could well be an X-Y problem
c
@igor.wojda Oh ok, thanks! I'll probably won't look at it right now, but I will refactor at some point. I'm trying not to get overwhelmed by too many things at the same time ^^
👍 1
a
that object saves a picture obtained in retrofit GET to a file
So, to write to a file in Android, you do not need Application context. Presumably you're using it to get the path to save to? You can either init your instance with the path, pass the path in during your method. You can also use dependency injection, but sounds like that would be yet another thing to learn at this point.
g
Just create an instance of class and pass context, with or without DI framework
c
Indeed, I might be doing it wrong.
I will probably try the singleton pattern to get it working, and then think of a way to decouple as much as I can the call to URL and the saving of the image.
a
Pass context only if you need it. But I doubt you do. Interface-segregation principle (ISP) "no client should be forced to depend on methods it does not use" Passing context, if all you need is a path, is an extreme ISP violation smell. Not to mention accidentally passing an activity to it could create a leak.
🙏 1
c
Yeah, I get what you mean. I'll create an imagesaver that will have the base path, and pass the file name in the method that download. It'll be easier.
j
a
I 👎 this advice because: 1. Anyone can make a global
var context: Context
. 2. No one should. 3. You certainly don't need a library for it. 4. Using/having a library for it just normalizes this anti-pattern and hides lint warnings for you. Doesn't mean the issues lint was warning you about go away. 5. Instead of lint, using this library comes with tons of warnings about gotchers in JavaDoc. Why not just follow best practices and not bypass lint warnings that are trying to help you. 6. Rather than understand their problem, you're assisting with their attempted solution. See http://xyproblem.info/
l
@Alan Evans 1. Yes, but Splitties AppCtx provides a
val
, not a
var
, and it is made so it can't leak memory. 2. This is not a
var
, so irrelevant. 3. The purpose of a library is reuse, which is a key consideration in Kotlin BTW, so you can copy paste again and again, but it is pointless if you can make it a library and reuse it. 4. Lint is not a compiler. It highlights potential issues, so by definition, it can be wrong, and that's why you can suppress the warnings it generates. 5. The library is documented, and it is counted in words, not tons. Also, this is not Javadoc, but a README, and KDoc. Also, the library follows best practices, and talks about it in the doc (that you probably didn't read before saying it is "tons of Javadoc"). The lint, as I said, highlights potential errors, and the library is not bypassing lint but removing irrelevant warning, as there's already a mechanism to ensure `appCtx`is the application
Context
by default and can never leak memory (which anyone's hand written
var context: Context
would likely not do). 6. I don't understand the point given all of the above.
a
private var internalCtx: Context? = null
l
@Alan Evans Yes, it is private to the library, and is initialized before
Application.onCreate()
. I have used this in production for years now. It never fails, and it saved my code from
Context
parameter/property pollution for all the places where all I need is the application
Context
.
a
Yes I see now that you exchange Lint for runtime errors. So this is safer than rolling your own. No offence intended. I am just anti-singleton/service locator, I prefer explicit dependencies. But that's just a style preference I suppose.
In case of OP, all they need is to know the path to save an image. I would inject that single property. Others, including the OPs initial attempt would provide global access to that path and more via the application context. I feel the second way makes it hard to see the dependencies of a class.
l
@Alan Evans No runtime error if you just use
appCtx
, it's not an "exchange". Don't take what lint says as absolute truth, you know what I said about it. BTW, lint isn't so safe as you can ignore its warnings easily, or even don't notice it. The
injectAsAppCtx
function however throws if you pass a context that can leak memory, of course. This function is there for testing and configuration overriding contexts. Having a need for this god
Context
object everywhere in Android is bad API design, but it's been designed more than 10 years ago. About accessing a path, I agree with you that it's better to make it an explicit dependency, and only then pass the actual value coming from the application Context, or another custom context (e.g. if you need direct boot support).
g
Having a need for this god
Context
object everywhere in Android is bad API design
We abstract all API of Context that we use in our projects and just use DI to pass implementations like for resources, starting intents, opening dialogs etc
👍 3