How to define context in a kotlin object - android

How can I define a context for the progressdialog within such an object
import dmax.dialog.SpotsDialog
object Constants {
//These are all the constants within our application
const val permission_request = 100
val firebaseAuth = FirebaseAuth.getInstance()
val progressDialog = SpotsDialog.Builder().setContext(thecontext).build()
}

You can give it a lateinit context property that you set in your Application class. Then make the property that's dependent on it Lazy. But in this case, it doesn't make sense, because a Dialog is transient. It wouldn't be a constant. You can't reuse dialogs, because Android destroys and recreates the Activities/Fragments that host them according to various lifecycle processes.
But if you do have something like a constant that needs a Context, this is how you could do it:
object Constants {
lateinit var context: Context
val foo by lazy { Foo(context) }
}
class MyApplication: Application() {
override fun onCreate() {
super.onCreate()
Constants.context = this
}
}
And make sure you set .MyApplication as the Application name in the manifest.

Related

Android Kotlin: Having a global Context object for all activities (best practice)

I have a global app settings class as follows:
class AppSettings : MultiDexApplication() {
override fun onCreate() {
super.onCreate()
instance = this
resourses = applicationContext.resources
outputPathCache = cacheDir.absolutePath
}
companion object {
lateinit var instance: AppSettings
private set
val context: Context
get() { return activityContext.get()!! }
lateinit var activityContext: WeakReference<Context>
var database: SQLiteDatabase? = null
var resourses: Resources? = null
private set
lateinit var dialog: AlertDialog
const val defLanguage = Enum.Language.ENGLISH
const val defIdLanguage = Enum.LanguageId.ENGLISH
const val screenshotFilename = "xxx"
const val actionBarTitleColor = "#0D0D0D"
const val footerColor = "#8a8a8a"
const val activityBackground = "#ffffff"
... whatever
}
}
And as you see I have a static Context variable as follows:
lateinit var activityContext: WeakReference<Context>
(I use WeakReference so the IDE doesn't complain about memory leaks).
And I have a constant Context like the next:
val context: Context
get() { return activityContext.get()!! }
I assign a value for the first time to activityContext in SplashActivity as follows (I do this because the first activity is a OnBoarding class that doesn't inherit from BaseActivity):
AppSettings.activityContext = WeakReference(this)
The same in BaseActivity onCreate (most of my activities inherit from this class):
AppSettings.activityContext = WeakReference(this)
And then, in any activity which extends BaseActivity I can use the context simply like this:
AppSettings.context
For the activities that doesn't inherit from BaseActivity I just initialise the context to be used in the activity in the same way as in Base, so I can always get it as "AppSettings.context".
The reason of not simply using "this" in all activities to get context (or to use any sort of Context creation in Base) is that I'm using MVVM and there are classes outside activities (like ViewModel) with methods that may need a context, and I just don't wan't to pass it as a parameter (this is why I'm expecting to have a global context that can be accessed anywhere).
Although I have just finished and I haven't fully tested yet, it is apparently working great, but I wonder to know if this is the recommended way to deal with this, or if there is a better approach to have a global Context.
There are some ways to achieve that, but I believe the most encouraged by google is with the dependency injection library dagger-hilt, which isn't hard to set-up, but saves a lot of time and prevents possible memory-leaks.
In order to inject context into any class later you just need to do:
class ExampleClass #Inject constructor(#ApplicationContext val context: Context) {}

How to get Context in onViewCreated

I'm attempting to configure a RecyclerView adapter and I have a line of code
val decorator = AppCompatResources.getDrawable(context, R.drawable.decorator)
This line of code will not work however as context is nullable and getDrawable requires context. I can think of 4 potential options for getting a non-nullable context in onViewCreated:
Wrap the code in context?.let {}
Wrap the code in activity?.applicationContext?.let {}
Use requireContext()
Get context from my application. My application class includes the following code:
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
lateinit var instance: MyApp
private set
val context: Context
get() = instance
}
My dev lead is not a fan of requireContext, and will request that I remove it if he sees it in a pull request. Is he wrong? Help me get Context smart, which of these options are the best and why?

Access the application context inside of a singleton

I have a singleton that I'm using to open a JSON asset and return it as a list. I need to access the application context in order to use the Asset Manager. I can't pass in the context because I'm calling it from a view model, which does not have access to the application context. I've done a lot of searching but I can't seem to find the answer.
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
object ProgramListService {
fun getProgramList(): List<ProgramList>? {
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val json = context.assets.open("programs/home.json").bufferedReader().use{ it.readText() }
val listType = Types.newParameterizedType(List::class.java, ProgramList::class.java)
val adapter: JsonAdapter<List<ProgramList>> = moshi.adapter(listType)
return adapter.fromJson(json)
}
}
Context is not good in a ViewModel, not even application context, nor is AndroidViewModel good to use. So you can pass AssetManager into your ViewModel which can then pass it into your ProgramListService, avoiding the need for context, but asset manager is still a bit weird to have in view model.
So you can skip the viewmodel and pass the application context directly into your singleton ProgramListService.
object ProgramListService {
lateinit var application: Application // Add this
...
}
And then from your activity's onCreate or wherever is best for your project,
ProgramListService.application = context.applicationContext as Application
Or like #Tenfour04 suggested application is safe to make a global property
You have the Context in ViewModel
public class HomeViewModel extends AndroidViewModel {
public HomeViewModel(Application application) {
super(application);
Context context = getApplication().getApplicationContext();
}}
I would hesitate to call what you have a singleton, since it doesn't carry any state. You've used an object to organize the namespace of what would be a static method in Java.
If your ViewModel is an AndroidViewModel, it comes with an Application instance, so you can use that as your context:
class MyViewModel(val application: Application): AndroidViewModel(application) {
fun foo() {
someRepoAccessCall(application)
}
}
Since the Application is safe to use as a singleton, you can alternatively create a global property for it that you initially set in onCreate().
lateinit var application: MyApplication
class MyApplication(): Application
override fun onCreate() {
super.onCreate()
application = this
}
}
Don't forget to assign the custom Application class in the manifest:
<application
android:name=".MyApplication"
...

Static like methods in Android application with kotlin

I am trying to add a "static" method to my MyApplication class in kotlin
I have added (as a property) the variable :
private var context: Context? = null
in method:
override fun onCreate()
I added:
context = applicationContext
then I add a companion object like this
companion object {
#JvmStatic fun getMyApplicationContext(): Context?
{
return MyApplication().context
}
}
when I call this method from other parts of the application like
MyApplication.getMyApplicationContext() it always returns null. I have gleaned all this from several sources but I am not sure if it is anywhere near correct or not.
It sounds like you want a global application context object. Now casting aside my dislike for global variables, I think you are pretty close.
I think you just need to add the variable into the MyApplication classes companion object and use that directly. You only need the #JvmField annotation if you're going to access the field from Java.
class MyApplication {
companion object {
#JvmField
var context: Context? = null
// Not really needed since we can access the variable directly.
#JvmStatic fun getMyApplicationContext(): Context? {
return context
}
}
override fun onCreate() {
...
MyApplication.context = appContext
}
}

Kotlin singleton application class

On Android I want to make my application class a singleton.
Making it like this:
object MyApplication: Application(){}
won't work. The following error is thrown at runtime:
java.lang.IllegalAccessException: private com....is not accessible from class android.app.Instrumentation.
Doing this is also not possible:
class MyApp: Application() {
private val instance_: MyApp
init{
instance_ = this
}
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree());
}
}
companion object{
fun getInstance() = instance_
}
}
How can I get an instance of my application class everywhere in my app? I would like to use MyApp.instance() instead of (applicationContext as MyApp).
Also an explanation why I want this: I have classes in my app. For example, a SharedPreference Singleton which is initialised with a context, and as it’s a singleton, it can't have arguments.
You can do the same thing you would do in Java, i.e. put the Application instance in a static field. Kotlin doesn't have static fields, but properties in objects are statically accessible.
class MyApp: Application() {
override fun onCreate() {
super.onCreate()
instance = this
}
companion object {
lateinit var instance: MyApp
private set
}
}
You can then access the property via MyApp.instance.
If you want to use it to access some static properties you have there: You will only have one instance of your Application, so simply use the name you gave to the class. Don't worry about it not being an actual singleton, you can use it the same way.
Example:
class MyApp : Application() {
companion object {
const val CONSTANT = 12
lateinit var typeface: Typeface
}
override fun onCreate() {
super.onCreate()
typeface = Typeface.createFromAsset(assets, "fonts/myFont.ttf")
}
}
Then you can use MyApp.CONSTANT and MyApp.typeface anywhere in your app.
-
If what you want is to use it as an application context you can create an extension property for Context:
val Context.myApp: MyApp
get() = applicationContext as MyApp
Then you can use myApp to get the the application context anywhere you have a context.
class AppController : Application() {
init {
instance = this
}
companion object {
private var instance: AppController? = null
fun applicationContext() : AppController {
return instance as AppController
}
}
override fun onCreate() {
super.onCreate()
}
}
You cannot do that because Android creates an Application instance using its parameterless constructor.
The problem you want to solve can be easily solved with DI. Just create instances with an injector so that the Context can be injected into objects as a dependency.

Categories

Resources