How to force initialization on App Context? - android

in order to use application context anywhere, I use this code:
class App : Application() {
companion object {
lateinit var instance: App private set
fun isInstanceInitialized() = ::instance.isInitialized
}
override fun onCreate() {
super.onCreate()
instance = this
}
}
But sometimes it throws an error.
kotlin.UninitializedPropertyAccessException: lateinit property instance has not been initialized
As suggested here, setting a delay (before calling App.instance) helps, but I would like to call something without having to wait for a set delay. And besides that, isInstanceInitialzed method always returns false..
What can I write in the beginning of onCreate method in MainActivity, in order to make sure that the instance variable is initialized?

Related

How to pass lambda as intent to start an activity in android using kotlin

I am writing a library that allow user to start my activity with pass in params and callbacks.
The architecture I been following is the MVVM. I dont have any issue with the MVVM pattern however, I am running into an occasional issue with the callback pass in from my caller.
class mainActivity :Activity {
// standard lifecycle
override fun onCreate(savedInstanceState: Bundle?) {
...
}
override fun onResume() {
// here I am accessing the static callback from companion object.
// but sometimes I am getting lateinit property callback has not been initialized
//exception.
callback.invoke()
}
companion object {
lateinit var callback: () -> Unit
//user will call my library from this method and provide me the callback
fun play(
callback: () -> Unit
){
callback = callback // here I assigned the callback so that my activity can use it later.
}
}
}
inside onResume I am accessing the callback. sometimes I am getting
lateinit property callback has not been initialized
however, I am not available to reproduce it 100% at all. What can be an issue? can you please shed some lights with me please?

Using 'this' as Context in the init block of activity?

I am developing an android application with kotlin.
I have a DereDatabaseHelper class which has init block that uses a context given through class parameter(?)
The DereDatabaseHelper is like this.
class DereDatabaseHelper(context: Context) {
val manifestFile: File
val fumensDBFile: File
val fumenFolder: File
val musicIDToInfo: MutableMap<Int, MusicInfo> = HashMap()
val fumenIDToMusicID: SparseIntArray = SparseIntArray()
init {
val datadir = context.getExternalFilesDir(null).parentFile.parentFile
The DereDatabaseHelper class is instantiated here in SongListActivity like this.
class SongListActivity : AppCompatActivity() {
var dereDatabaseHelper : DereDatabaseHelper
init {
dereDatabaseHelper = DereDatabaseHelper(this)
}
I thought that this code was correct, but this codes throws NullPointerException.
java.lang.NullPointerException: Attempt to invoke virtual method
'java.io.File android.content.Context.getExternalFilesDir(java.lang.String)'
on a null object reference at
android.content.ContextWrapper.getExternalFilesDir(ContextWrapper.java:253)
at com.kyhsgeekcode.dereinfo.model.DereDatabaseHelper.<init>(DereDatabaseHelper.kt:21)
at com.kyhsgeekcode.dereinfo.SongListActivity.<init>(SongListActivity.kt:31)
Is this null when the execution is in init block and what initialization style should I use to fix this?
Never use the constructor of an Activity to do anything that involves the Context. Android instantiates Activities using their sole empty constructor (via reflection), and then sets up the activity's various fields before it ever calls onCreate(). Your first safe entry point to do anything in your Activity is in onCreate().
You also can't call methods of the Activity (which is itself a Context) in the constructor.
You also can't use the context in any way to even set up properties, because they'll try to access the context before onCreate:
class MyActivity: AppCompatActivity() {
val assets: AssetManager = getAssets() // This will cause a crash
}
To avoid having to make your property nullable, you can do either of the following, which allow you to avoid having your class instantiated until after onCreate() is called:
class SongListActivity : AppCompatActivity() {
lateinit var dereDatabaseHelper : DereDatabaseHelper
override fun onCreate() {
super.onCreate
dereDatabaseHelper = DereDatabaseHelper(this)
}
or
class SongListActivity : AppCompatActivity() {
val dereDatabaseHelper by lazy { DereDatabaseHelper(this) }
}
Activities are not completely initialized by the constructor or in your case init block.
Android system initializes activities and then calls the onCreate method. So you should do the following
override fun onCreate(savedInstanceState: Bundle?) {
// create instance of DareDatabaseHelper
}
Why it doesn't work with the constructor?
consider the following code snippet
var myActivity = MyActivity() // This doesn't start MainActivity
// This is how you start an activity
val intent = Intent(context, MyActivity::class.java)
startActivity(intent)
When you start any activity you never instantiate the activity class, why?
Because that is the responsibility of android system, when you do startActivity(intent) android system instantiates your activity class using the default constructor and then does all the initialization (ie. providing context) And once the activity is completely initialized the onCreate method of your activity is called where you can do your end of initialization.

Kotlin: define a coroutine scope in App class

Is it a good practice to declare a coroutine scope inside the App class instead of using a Globascope?
class App : Application() {
val applicationJob = Job()
companion object {
lateinit var instance: App
private set
lateinit var appDefaultScope: CoroutineScope
}
override fun onCreate() {
super.onCreate()
instance = this#App
appDefaultScope = CoroutineScope(Dispatchers.Default + applicationJob)
}
}
Here I defined a variable appDefaultScope and initialized it in the App's onCreate
So anywhere in my code I could always do:
App.appDefaultScope.launch {
// some operations
}
The main difference with a GlobalScope that I see is that here I can always cancel the job and shut down all potentially stuck coroutines.
Are there are better alternatives to this?
The reason is that i have some object functions that use coroutines and are not called from an activity (then I cannot provide them with a scope defined inside the activity) but need a scope for their operations.
An example of such object function is a Log that writes on a local database that can be called almost everywhere in the App.

Kotlin: How to check variable with lateinit property is initialized or not

I have a variable that is declared like
private lateinit var apiDisposable: Disposable
and then in onPause() method, I am doing
override fun onPause() {
super.onPause()
if (!apiDisposable.isDisposed)
apiDisposable.dispose()
}
But I get this
kotlin.UninitializedPropertyAccessException: lateinit property
apiDisposable has not been initialized
Can anyone tell me how could I check if this variable is initialized or not? Is there any method like isInitialised()
Any help would be appreciated
Declare your property as a simple property of a nullable type:
private var apiDisposable: Disposable? = null
Call the method using the safe call notation:
override fun onPause() {
super.onPause()
apiDisposable?.dispose()
}
lateinit is reserved for variables that are guaranteed to be initialized, if this is not your case - don't use it.
if(::apiDisposable.isInitialized)
will solve your problem.
In general,
::<lateinit variable name>.isInitialized is used to check if it has been initialized.

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
}
}

Categories

Resources