Is using companion object in activity and application class a good way? - android

I used a lot of static data in Activities, Adapters, Application, etc with like
companion object{
const val SEND_MY_DATA = "sendta"
const val SEND_MY_DATA_1 = "sendta1"
const val SEND_MY_DATA_2 = "sendta2"
}
to have common name for intent extras to match the same name between two activities. So, this static data are used in the activity & in another activity, and even some adapters.
And also I used this in Application class like
// this is used somewhere.
fun updateContext(){
appContext = applicationContext
}
companion object{
var appContext: Context? = null
fun myFunction(context: Context){
// use context param here.
}
}
Is this a bad approach or not? Is there any better way to improve this?

Just a note about saving data in the Application class, I encountered this by dealing with local resources in Room.
Generally, as in the case of the linked question, it can be a good solution (Android Studio shows a memory leak warning).
According to your needs you have to be careful to save data in this way, because Android OS can actually kill processes, including your Application instance.
To determine which processes should be killed when low on memory, Android places each process into an "importance hierarchy" based on the components running in them and the state of those components.
Check Processes and Application Lifecycle for more information.
A complete discussion about this can be found on this post.

If you're going to make a static application Context reference, I think this is cleaner:
companion object {
lateinit var context: Context
private set
}
override fun onCreate() {
super.onCreate()
context = applicationContext
}
But if you use dependency injection, you shouldn't need it. The singleton Context pattern makes unit testing difficult.
As for storing your constants, companion objects are fine. They do result in an extra class that's compiled, but that should be trivial since you shouldn't have very many activities.

Related

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

Is it safe to store data in a kotlin object?

I have a question regarding objects in kotlin, to which I couldn't find a satisfying answer so far.
It's basically the following scenario: I've got some user data, that I want to make available through the entire app, without having to pass it through activities.
To achieve this, I created an object with two properties that are instantiated with the user's data in the startup activity. Is this a safe way to store and make the user's data available for all activities or will it get lost on the way?
Example:
object CurrentUserManager {
lateinit var userId: String,
lateinit var userName: String
}
LoginActivity {
...
onCreate(...){
val user = ApiCall.Login();
CurrentUserManager.userId = user.id
CurrentUserManager.userName = user.name
}
}
MainActivity {
...
onCreate(...){
Toast.makeText(this, "Hello ${CurrentUserManager.userName} with ID: ${CurrentUserManager.userId}", Toast.LENGTH_SHORT).show()
}
}
Is this unsafe/bad practice and if so why and which pattern should I use to achieve the expected outcome?
Thanks,
lionthefox
Short answer: it's safe as long as your Android process does not end.
Long answer: this boils down to the discussion about lifetime of singletons and static variables in Java. There have already been some answers to this very question, so I won't re-iterate those here:
Android static object lifecycle
Is it safe to use static class variables in an android application
Lifetime of a static variable in Android
use intent put extras or maintain a static Object

Will there be any memory leaks if I access my resources statically from My Application class?

Will there be any memory leaks if I access resources statically from My Application class like so:
class App : Application() {
companion object {
fun getResources(): Resources {
return this.getResources()
}
}
}
I was looking for a way to access my resources directly from my view models without passing a context object I just had to use AndroidViewModel instead of ViewModel from android arch components.
Thanks all.
Companion objects are not static, as the documentation describes:
Note that, even though the members of companion objects look like static members in other languages, at runtime those are still instance members of real objects, and can, for example, implement interfaces
You can't use companion object like this to get access to the App class. In your example, this refers to companion object itself, thus you're creating endless recursive call: this.getResources() just calls itself since this == App.Companion.
You cannot access App instance from its companion object, but you can access your companion instance from App class. That means, if you want to access your app context globally, you have to do smth like this:
class App {
override fun onCreate() {
super.onCreate()
appContext = this
}
companion object {
lateinit var appContext: Context
fun getResources(): Resources = appContext.resources
}
}

Do not place Android context classes in static fields

I need to access to the context of an application, but doing it this way it says "Do not place Android context classes in static fields" on the third line. Tried to remove the private val but without it I can't access to the context on the copyDatabase function.
I need the context to copy a database in the assets folder to the data folder of the application.
class Database constructor(private val ctx: Context) : ManagedSQLiteOpenHelper(ctx, "dex.db", null, 1) {
companion object {
private var instance: Database? = null
#Synchronized
fun getInstance(ctx: Context): Database {
if (instance == null) {
instance = Database(ctx.applicationContext)
}
return instance!!
}
}
private fun copyDatabase() {
val input = ctx.assets.open("databases/dex.db")
FileOutputStream(ctx.getDatabasePath("dex.db").path).use { out ->
input.copyTo(out)
}
}
}
Thanks
By using a companion object, whose lifetime equals the lifetime of the loaded Database class, you have created a lifecycle mismatch between the Context you capture in the database instance and the database instance itself.
When your application is put to background and restored, or if you just rotate the device, the context (typically the Activity instance) will be destroyed without destroying the whole application, so the database instance will survive with a disposed context. This will reliably lead to application failure.
Although you could be careful to use an Application context instead, which would most probably match the lifecycle, the general practice of retaining the context instance is ill-advised, hence the warning.
Instead put the database instance as a property of your main activity class, or, alternatively, commit to using a dependency injection framework that will deal with this as a separate concern.
As an aside, your current code uses a broken variant of the double-checking lazy initialization idiom. If you need lazy initialization, in Kotlin you should always leave this to the by lazy property delegate instead of rolling your own.
Keeping the Context inside a static variable prevent the garbage collector to free the memory afterwards and will lead to a memory leak. You should pass a Context to your constructor and initialize everything inside it and should not have to retain it inside a variable.

Static reference to application context and resources in a companion object of Application

I've read a number of articles and stackoverflow posts regarding this question (mainly context instead of resources). A lot of people said it's OK because the Application class and the application context should always be alive throughout the life of the app, but some still discourage doing this sighting unknown possibilities (which made me doubtful - hence this post).
On the other hand, passing Context to every Object's function is quite daunting. In Java, one way to overcome this was to do something like UtilClass.getInstance(context) but you can't do that with Kotlin objects and I'm not too sure about implementing Kotlin Singletons with Arguments.
So, I have the following code:
class App : MultiDexApplication() {
override fun onCreate() {
super.onCreate()
_resources = resources
_context = applicationContext
//..other code
}
companion object {
val resources: Resources
get() = _resources!!
val context: Context
get() = _context!!
private var _resources: Resources? = null
private var _context: Context? = null
}
}
As you can see, I'm keeping a static reference to the application context and resources in a companion object when onCreate is called. With this, I'm able to (as examples):
call App.context in a util object that make API calls with Ion.
call App.resources in a util object that uses resource strings.
It's a convenience as compared to passing Context and/or Resources every time I call an object's method. Is this a safe solution? If not, are there any better alternatives?

Categories

Resources